Browse Source

final_project

Levshin95 1 year ago
parent
commit
d76164725e
76 changed files with 60191 additions and 0 deletions
  1. 23 0
      HW_REACT/react002/.gitignore
  2. 70 0
      HW_REACT/react002/README.md
  3. 28700 0
      HW_REACT/react002/package-lock.json
  4. 38 0
      HW_REACT/react002/package.json
  5. BIN
      HW_REACT/react002/public/favicon.ico
  6. 43 0
      HW_REACT/react002/public/index.html
  7. BIN
      HW_REACT/react002/public/logo192.png
  8. BIN
      HW_REACT/react002/public/logo512.png
  9. 25 0
      HW_REACT/react002/public/manifest.json
  10. 3 0
      HW_REACT/react002/public/robots.txt
  11. 46 0
      HW_REACT/react002/src/App.css
  12. 57 0
      HW_REACT/react002/src/App.js
  13. 8 0
      HW_REACT/react002/src/App.test.js
  14. 13 0
      HW_REACT/react002/src/index.css
  15. 17 0
      HW_REACT/react002/src/index.js
  16. 1 0
      HW_REACT/react002/src/logo.svg
  17. 13 0
      HW_REACT/react002/src/reportWebVitals.js
  18. 5 0
      HW_REACT/react002/src/setupTests.js
  19. 23 0
      final_project/.gitignore
  20. 70 0
      final_project/README.md
  21. 29279 0
      final_project/package-lock.json
  22. 44 0
      final_project/package.json
  23. BIN
      final_project/public/favicon.ico
  24. 47 0
      final_project/public/index.html
  25. BIN
      final_project/public/logo192.png
  26. BIN
      final_project/public/logo512.png
  27. 25 0
      final_project/public/manifest.json
  28. 3 0
      final_project/public/robots.txt
  29. 193 0
      final_project/src/App.css
  30. 103 0
      final_project/src/App.js
  31. 8 0
      final_project/src/App.test.js
  32. 58 0
      final_project/src/components/Header.jsx
  33. 45 0
      final_project/src/components/PrivateHeader.jsx
  34. 40 0
      final_project/src/components/PublicHeader.jsx
  35. 23 0
      final_project/src/graphQl/getGQL.js
  36. 19 0
      final_project/src/guard/AuthGuard.jsx
  37. 5 0
      final_project/src/helpers/jwt-decode.js
  38. 3 0
      final_project/src/history/index.js
  39. BIN
      final_project/src/img/IMG_1920.JPG
  40. BIN
      final_project/src/img/IMG_1921.JPG
  41. BIN
      final_project/src/img/IMG_1922.JPG
  42. BIN
      final_project/src/img/bacground-chat.png
  43. BIN
      final_project/src/img/back.JPG
  44. BIN
      final_project/src/img/home.jpg
  45. BIN
      final_project/src/img/logo.jpg
  46. BIN
      final_project/src/img/sticker.webp
  47. 13 0
      final_project/src/index.css
  48. 17 0
      final_project/src/index.js
  49. 1 0
      final_project/src/logo.svg
  50. 48 0
      final_project/src/pages/AbouteMePage.jsx
  51. 127 0
      final_project/src/pages/ChatInfoPage.jsx
  52. 77 0
      final_project/src/pages/ChatPage.jsx
  53. 66 0
      final_project/src/pages/CreateChatPage.jsx
  54. 17 0
      final_project/src/pages/HomePage.jsx
  55. 60 0
      final_project/src/pages/LoginPage.jsx
  56. 4 0
      final_project/src/pages/MainPage.jsx
  57. 126 0
      final_project/src/pages/OpenChatPage.jsx
  58. 47 0
      final_project/src/pages/RegisterPage.jsx
  59. 13 0
      final_project/src/reportWebVitals.js
  60. 5 0
      final_project/src/setupTests.js
  61. 52 0
      final_project/src/store/actions/action-aboutMe.js
  62. 33 0
      final_project/src/store/actions/action-chats-promise.js
  63. 29 0
      final_project/src/store/actions/action-chats.js
  64. 39 0
      final_project/src/store/actions/action-create-chat.js
  65. 39 0
      final_project/src/store/actions/action-create-msg.js
  66. 51 0
      final_project/src/store/actions/action-login-promise.js
  67. 4 0
      final_project/src/store/actions/action-logout.js
  68. 33 0
      final_project/src/store/actions/action-promise.js
  69. 17 0
      final_project/src/store/actions/action-register-promise.js
  70. 27 0
      final_project/src/store/actions/action-search-user.js
  71. 1 0
      final_project/src/store/actions/actionMsgOn.js
  72. 60 0
      final_project/src/store/index.js
  73. 22 0
      final_project/src/store/reducers/auth-reducer.js
  74. 73 0
      final_project/src/store/reducers/chat-reducer.js
  75. 13 0
      final_project/src/store/reducers/local-store-reducer.js
  76. 27 0
      final_project/src/store/reducers/promise-reducer.js

+ 23 - 0
HW_REACT/react002/.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 70 - 0
HW_REACT/react002/README.md

@@ -0,0 +1,70 @@
+# Getting Started with Create React App
+
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.\
+Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
+
+The page will reload when you make changes.\
+You may also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.\
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.\
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.\
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can't go back!**
+
+If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
+
+You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
+
+### Analyzing the Bundle Size
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
+
+### Making a Progressive Web App
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
+
+### Advanced Configuration
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
+
+### Deployment
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
+
+### `npm run build` fails to minify
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

File diff suppressed because it is too large
+ 28700 - 0
HW_REACT/react002/package-lock.json


+ 38 - 0
HW_REACT/react002/package.json

@@ -0,0 +1,38 @@
+{
+  "name": "react002",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@testing-library/jest-dom": "^5.16.5",
+    "@testing-library/react": "^13.3.0",
+    "@testing-library/user-event": "^13.5.0",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-scripts": "5.0.1",
+    "web-vitals": "^2.1.4"
+  },
+  "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"
+    ]
+  }
+}

BIN
HW_REACT/react002/public/favicon.ico


+ 43 - 0
HW_REACT/react002/public/index.html

@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta
+      name="description"
+      content="Web site created using create-react-app"
+    />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <!--
+      manifest.json provides metadata used when your web app is installed on a
+      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <title>React App</title>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+  </body>
+</html>

BIN
HW_REACT/react002/public/logo192.png


BIN
HW_REACT/react002/public/logo512.png


+ 25 - 0
HW_REACT/react002/public/manifest.json

@@ -0,0 +1,25 @@
+{
+  "short_name": "React App",
+  "name": "Create React App Sample",
+  "icons": [
+    {
+      "src": "favicon.ico",
+      "sizes": "64x64 32x32 24x24 16x16",
+      "type": "image/x-icon"
+    },
+    {
+      "src": "logo192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    },
+    {
+      "src": "logo512.png",
+      "type": "image/png",
+      "sizes": "512x512"
+    }
+  ],
+  "start_url": ".",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}

+ 3 - 0
HW_REACT/react002/public/robots.txt

@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:

+ 46 - 0
HW_REACT/react002/src/App.css

@@ -0,0 +1,46 @@
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  height: 40vmin;
+  pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  .App-logo {
+    animation: App-logo-spin infinite 20s linear;
+  }
+}
+
+.error{
+  background-color: red;
+}
+
+input{
+outline: none;
+}
+
+.App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}

+ 57 - 0
HW_REACT/react002/src/App.js

@@ -0,0 +1,57 @@
+import React, {useState, useEffect} from 'react';
+import logo from './logo.svg';
+import './App.css';
+
+
+
+ const Spoiler = ({header = '+', open, children}) => {
+    const [display, setDisplay] = useState(open)
+
+    return (
+        <div>
+            <div onClick={() => setDisplay(!display)}>
+                {header}
+            </div>
+            <div>
+                {display ? children : null}
+            </div>    
+        </div>
+    ) 
+}; 
+
+const RangeInput = ({min, max}) => {
+    const [inputNumber, setInputNumber] = useState('')
+    return (
+        <div>
+            <input type = 'text' style ={{ background: inputNumber.length < min || inputNumber.length > max ? 'red' : null  }}
+            onChange={(e) => setInputNumber(e.target.value)}
+            />
+        </div>
+    )
+}
+
+
+function App() {
+    return (
+        
+            <div className="App">
+                <Spoiler header={<h1>Заголовок</h1>} open>
+                    Контент 1
+                    <p>
+                    лорем ипсум траливали и тп.лорем ипсум траливали и тп.лорем ипсум траливали и тп.лорем ипсум траливали и тп.лорем ипсум траливали и тп.
+                    </p>
+                </Spoiler>
+
+                <Spoiler>
+                    <h2>Контент 2</h2>
+                    <p>
+                    лорем ипсум траливали и тп.лорем ипсум траливали и тп.лорем ипсум траливали и тп.лорем ипсум траливали и тп.лорем ипсум траливали и тп.
+                    </p>
+                </Spoiler>
+                <RangeInput min={2} max={10} />
+                {/* <RangeInputParent></RangeInputParent> */}
+            </div>
+        
+    );
+}
+export default App;

+ 8 - 0
HW_REACT/react002/src/App.test.js

@@ -0,0 +1,8 @@
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+  render(<App />);
+  const linkElement = screen.getByText(/learn react/i);
+  expect(linkElement).toBeInTheDocument();
+});

+ 13 - 0
HW_REACT/react002/src/index.css

@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}

+ 17 - 0
HW_REACT/react002/src/index.js

@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();

File diff suppressed because it is too large
+ 1 - 0
HW_REACT/react002/src/logo.svg


+ 13 - 0
HW_REACT/react002/src/reportWebVitals.js

@@ -0,0 +1,13 @@
+const reportWebVitals = onPerfEntry => {
+  if (onPerfEntry && onPerfEntry instanceof Function) {
+    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+      getCLS(onPerfEntry);
+      getFID(onPerfEntry);
+      getFCP(onPerfEntry);
+      getLCP(onPerfEntry);
+      getTTFB(onPerfEntry);
+    });
+  }
+};
+
+export default reportWebVitals;

+ 5 - 0
HW_REACT/react002/src/setupTests.js

@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';

+ 23 - 0
final_project/.gitignore

@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*

+ 70 - 0
final_project/README.md

@@ -0,0 +1,70 @@
+# Getting Started with Create React App
+
+This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+
+## Available Scripts
+
+In the project directory, you can run:
+
+### `npm start`
+
+Runs the app in the development mode.\
+Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
+
+The page will reload when you make changes.\
+You may also see any lint errors in the console.
+
+### `npm test`
+
+Launches the test runner in the interactive watch mode.\
+See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
+
+### `npm run build`
+
+Builds the app for production to the `build` folder.\
+It correctly bundles React in production mode and optimizes the build for the best performance.
+
+The build is minified and the filenames include the hashes.\
+Your app is ready to be deployed!
+
+See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
+
+### `npm run eject`
+
+**Note: this is a one-way operation. Once you `eject`, you can't go back!**
+
+If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
+
+Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
+
+You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
+
+## Learn More
+
+You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
+
+To learn React, check out the [React documentation](https://reactjs.org/).
+
+### Code Splitting
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
+
+### Analyzing the Bundle Size
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
+
+### Making a Progressive Web App
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
+
+### Advanced Configuration
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
+
+### Deployment
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
+
+### `npm run build` fails to minify
+
+This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

File diff suppressed because it is too large
+ 29279 - 0
final_project/package-lock.json


+ 44 - 0
final_project/package.json

@@ -0,0 +1,44 @@
+{
+  "name": "final_project",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@testing-library/jest-dom": "^5.16.5",
+    "@testing-library/react": "^13.4.0",
+    "@testing-library/user-event": "^13.5.0",
+    "bootstrap": "^5.2.1",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-redux": "^8.0.2",
+    "react-router-dom": "^5.3.0",
+    "react-scripts": "5.0.1",
+    "redux": "^4.2.0",
+    "redux-thunk": "^2.4.1",
+    "socket.io-client": "^4.5.2",
+    "web-vitals": "^2.1.4"
+  },
+  "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"
+    ]
+  }
+}

BIN
final_project/public/favicon.ico


+ 47 - 0
final_project/public/index.html

@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta
+      name="description"
+      content="Web site created using create-react-app"
+    />
+    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+    <!--
+      manifest.json provides metadata used when your web app is installed on a
+      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
+    -->
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <!--
+      Notice the use of %PUBLIC_URL% in the tags above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <title>React App</title>
+    
+    <script src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js'></script>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start` or `yarn start`.
+      To create a production bundle, use `npm run build` or `yarn build`.
+    -->
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
+  </body>
+</html>

BIN
final_project/public/logo192.png


BIN
final_project/public/logo512.png


+ 25 - 0
final_project/public/manifest.json

@@ -0,0 +1,25 @@
+{
+  "short_name": "React App",
+  "name": "Create React App Sample",
+  "icons": [
+    {
+      "src": "favicon.ico",
+      "sizes": "64x64 32x32 24x24 16x16",
+      "type": "image/x-icon"
+    },
+    {
+      "src": "logo192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    },
+    {
+      "src": "logo512.png",
+      "type": "image/png",
+      "sizes": "512x512"
+    }
+  ],
+  "start_url": ".",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}

+ 3 - 0
final_project/public/robots.txt

@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:

+ 193 - 0
final_project/src/App.css

@@ -0,0 +1,193 @@
+.App {
+  text-align: center;
+}
+
+.App-logo {
+  height: 40vmin;
+  pointer-events: none;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  .App-logo {
+    animation: App-logo-spin infinite 20s linear;
+  }
+}
+
+.App-header {
+  background-color: #282c34;
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: calc(10px + 2vmin);
+  color: white;
+}
+
+.App-link {
+  color: #61dafb;
+}
+
+@keyframes App-logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+.header {
+  padding-bottom: 57px;
+}
+
+a {
+  text-decoration: none;
+
+}
+
+body {
+  background-color: rgb(9, 6, 12);
+}
+
+h1,
+h2,
+label {
+  color: white;
+}
+
+.avatar {
+
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding-bottom: 20px;
+}
+
+.drop-area {
+  width: 30vh;
+  height: 30vh;
+  border: 5px solid white;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 50%;
+}
+
+.chatLi {
+  list-style-type: none;
+  border-bottom: 2px solid black;
+}
+
+.chatPage {
+  display: flex;
+
+}
+
+.hrefChat {
+  color: black
+}
+
+.chatLi:hover {
+  background-color: rgb(151, 182, 238);
+  color: black;
+}
+
+.chatAside {
+  overflow: auto;
+  padding: 0;
+  flex-basis: 50%;
+  background-color: rgb(197, 219, 248);
+  color: black;
+  min-height: 92vh;
+  border-right: 2px solid black;
+}
+
+.chatMain {
+  max-height: 92vh;
+  min-height: 80vh;
+  flex-basis: 90%;
+  display: flex;
+  flex-direction: column;
+
+}
+
+.msg {
+  color: white
+}
+
+.msgLi {
+  display: flex;
+  flex-direction: column;
+  background-color: white;
+  border-radius: 25px;
+  max-width: 80%;
+  border: 2px black solid;
+}
+
+.msgOwner {
+  color: red;
+}
+
+.img-label {
+  padding-top: 100px;
+  max-width: 400px;
+  max-height: 600px;
+}
+
+.divHome {
+  height: 100vh
+}
+
+.linkToCreate {
+  z-index: 1;
+  background-color: #0d6efd;
+  max-width: 100%;
+  border-radius: 0;
+  color: black;
+  height: 50px;
+  font-weight: bolder
+}
+
+.linkToCreate:hover {
+  color: black;
+}
+
+.sendMsg {
+  z-index: 1;
+  border-radius: 0;
+  color: black;
+  padding: 10px;
+  font-weight: bolder
+}
+
+
+.msgUl {
+  display: flex;
+  flex-direction: column-reverse;
+}
+
+.membersChat {
+  border: 2px solid white;
+}
+
+.chatPrewiew {
+  padding-top: 200px;
+}
+
+.dropdown-li {
+  padding-left: 20px;
+}
+
+.home-logo {
+  padding-top: 100px;
+  max-width: 400px;
+}
+
+.ava {
+  max-width: 200px;
+  max-height: 200px;
+  border-radius: 20%;
+  padding-bottom: 20px;
+}

+ 103 - 0
final_project/src/App.js

@@ -0,0 +1,103 @@
+import React, { useEffect } from 'react';
+import './App.css';
+import { Router, Switch, Route } from 'react-router-dom';
+import { Provider, connect } from "react-redux";
+import { socket } from './store';
+
+import { LoginPage } from './pages/LoginPage';
+import { RegisterPage } from './pages/RegisterPage';
+import { ChatPage } from './pages/ChatPage';
+import { OpenChatPage } from './pages/OpenChatPage';
+import { ChatInfoPage } from './pages/ChatInfoPage'
+import { CreateChatPage } from './pages/CreateChatPage';
+import { Header } from './components/Header';
+import { PublicHeader } from './components/PublicHeader'
+import { PrivateHeader } from './components/PrivateHeader';
+import { PageAboutMe } from './pages/AbouteMePage'
+import { HomePage } from './pages/HomePage';
+
+import { actionFullLogin, actionAuthLogin } from './store/actions/action-login-promise'
+import { actionAuthLogout } from './store/actions/action-logout'
+import { actionRegister } from './store/actions/action-register-promise'
+import { actionCreateChat } from './store/actions/action-create-chat';
+import { actionCreateMessage } from './store/actions/action-create-msg';
+import { actionAboutMe } from './store/actions/action-aboutMe';
+import { actionSearchUser } from './store/actions/action-search-user';
+
+import store from './store'
+import { history } from './history'
+
+const HeaderSwitch = () => {
+
+    return (
+        <Switch>
+            <Route path="/login" component={PublicHeader} exact />
+            <Route path="/registration" component={PublicHeader} exact />
+            <Route path="/chat" component={PrivateHeader} exact />
+            <Route path="/chat/:id" component={PrivateHeader} exact />
+            <Route path="/chat/:id/info" component={PrivateHeader} exact />
+            <Route path="/create-chat" component={PrivateHeader} exact />
+            <Route path="/about-me" component={PrivateHeader} exact />
+            <Route path="/" component={PublicHeader} exact />
+        </Switch>
+    )
+}
+
+const MainSwitch = ({ token }) => {
+    if (token) {
+        console.log("сокет")
+        socket.emit("jwt", token)
+    }
+    return (
+        <>
+            <Switch>
+                <div className="App">
+                    <Route path="/login" component={CLoginForm} exact />
+                    <Route path="/registration" component={CRegisterForm} exact />
+                    <Route path="/chat" component={CPageChat} exact />
+                    <Route path="/chat/:id" component={COpenPageChat} exact />
+                    <Route path="/chat/:id/info" component={CPageChatInfo} exact />
+                    <Route path="/create-chat" component={CCreateChat} exact />
+                    <Route path="/about-me" component={CPageAboutMe} exact />
+                    <Route path="/" component={HomePage} exact />
+                </div>
+            </Switch>
+        </>
+    )
+}
+
+
+const CLoginForm = connect(null, { onLogin: actionFullLogin, outLogin: actionAuthLogout })(LoginPage);
+const CRegisterForm = connect(null, { onRegister: actionRegister })(RegisterPage)
+const CPageAboutMe = connect(state => ({ userName: state.promise.userData?.payload.login, avatar: state.promise.userData?.payload.avatar, chats: state.promise.userData?.payload.chats }), { getData: actionAboutMe })(PageAboutMe)
+const CPageChat = connect(state => ({ chats: state.chat.userData?.payload.chats }), { getData: actionAboutMe })(ChatPage)
+const COpenPageChat = connect(state => ({ chats: state.chat.userData?.payload.chats, onCreateMsg: actionCreateMessage }), { getData: actionAboutMe })(OpenChatPage)
+const CPageChatInfo = connect(state => ({ chats: state.chat.userData?.payload.chats, us: state.promise.searchUs?.payload?._id, onSearchUser: actionSearchUser }), { getData: actionAboutMe })(ChatInfoPage)
+const CCreateChat = connect(null, { onCreate: actionCreateChat })(CreateChatPage)
+const CMainSwitch = connect((state) => ({ token: state.auth.token || null }))(
+    MainSwitch
+);
+
+
+function App() {
+
+    useEffect(() => {
+        if (localStorage.authToken) {
+            store.dispatch(actionAuthLogin(localStorage.authToken));
+
+        }
+    }, [])
+
+    return (
+        <Provider store={store}>
+            <Router history={history}>
+
+                <HeaderSwitch />
+                <CMainSwitch />
+
+            </Router>
+        </Provider>
+    );
+}
+
+export default App;

+ 8 - 0
final_project/src/App.test.js

@@ -0,0 +1,8 @@
+import { render, screen } from '@testing-library/react';
+import App from './App';
+
+test('renders learn react link', () => {
+  render(<App />);
+  const linkElement = screen.getByText(/learn react/i);
+  expect(linkElement).toBeInTheDocument();
+});

+ 58 - 0
final_project/src/components/Header.jsx

@@ -0,0 +1,58 @@
+import { useState } from 'react';
+import {  Link } from 'react-router-dom';
+import { history } from '../App';
+import '../App.css';
+import store from '../store'
+import {actionAuthLogout} from '../store/actions/action-logout'
+
+export const Header = () => {
+
+    const logOut = () => {
+        store.dispatch(actionAuthLogout())
+    }
+
+    return (
+        <header className="header">
+            <nav className="navbar navbar-dark bg-dark fixed-top">
+                <div className="container-fluid">
+                    <a className="navbar-brand" href="/">A-Level Chat</a>
+                    <button className="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasDarkNavbar" aria-controls="offcanvasDarkNavbar">
+                        <span className="navbar-toggler-icon"></span>
+                    </button>
+                    <div className="offcanvas offcanvas-end text-bg-dark" tabIndex="-1" id="offcanvasDarkNavbar" aria-labelledby="offcanvasDarkNavbarLabel">
+                        <div className="offcanvas-header">
+                            <button type="button" className="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
+                        </div>
+                        <div className="offcanvas-body">
+                            <ul className="navbar-nav justify-content-end flex-grow-1 pe-3">
+                                <li className="nav-item">
+                                    <Link to="/" className="nav-link">Home</Link>
+                                </li>
+                                <li className="nav-item dropdown">
+                                    <a className="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
+                                        Enter
+                                    </a>
+                                    <ul className="dropdown-menu dropdown-menu-dark">
+                                        <li className='dropdown-li'><Link to="/login" className="nav-link">Login</Link></li>
+                                        <li className='dropdown-li'><Link to="/registration" className="nav-link">Registration</Link></li>
+                                        
+                                    </ul>
+                                </li>
+                                <li className="nav-item">
+                                    <Link to="/about-me" className="nav-link">About Me</Link>
+                                </li>
+                                <li className="nav-item">
+                                    <Link to="/chat" className="nav-link">Chat</Link>
+                                </li>
+                                <li className="nav-item">
+                                    <a className="nav-link" onClick={logOut}>Выйти</a>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </nav>
+        </header>
+    )
+}
+

+ 45 - 0
final_project/src/components/PrivateHeader.jsx

@@ -0,0 +1,45 @@
+import {  Link } from 'react-router-dom';
+import '../App.css';
+import store from '../store'
+import {actionAuthLogout} from '../store/actions/action-logout'
+
+export const PrivateHeader = () => {
+
+    const logOut = () => {
+        store.dispatch(actionAuthLogout())
+    }
+
+    return (
+        <header className="header">
+            <nav className="navbar navbar-dark bg-dark fixed-top">
+                <div className="container-fluid">
+                    <a className="navbar-brand" href="/">A-Level Chat</a>
+                    <button className="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasDarkNavbar" aria-controls="offcanvasDarkNavbar">
+                        <span className="navbar-toggler-icon"></span>
+                    </button>
+                    <div className="offcanvas offcanvas-end text-bg-dark" tabIndex="-1" id="offcanvasDarkNavbar" aria-labelledby="offcanvasDarkNavbarLabel">
+                        <div className="offcanvas-header">
+                            <button type="button" className="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
+                        </div>
+                        <div className="offcanvas-body">
+                            <ul className="navbar-nav justify-content-end flex-grow-1 pe-3">
+                                <li className="nav-item">
+                                    <Link to="/" className="nav-link">Home</Link>
+                                </li>
+                                <li className="nav-item">
+                                    <Link to="/about-me" className="nav-link">About Me</Link>
+                                </li>
+                                <li className="nav-item">
+                                    <Link to="/chat" className="nav-link">Chat</Link>
+                                </li>
+                                <li className="nav-item">
+                                    <a className="nav-link" onClick={logOut}>Выйти</a>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </nav>
+        </header>
+    )
+}

+ 40 - 0
final_project/src/components/PublicHeader.jsx

@@ -0,0 +1,40 @@
+import {  Link } from 'react-router-dom';
+import '../App.css';
+
+
+export const PublicHeader = () => {
+
+    return (
+        <header className="header">
+            <nav className="navbar navbar-dark bg-dark fixed-top">
+                <div className="container-fluid">
+                    <a className="navbar-brand" href="/">A-Level Chat</a>
+                    <button className="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasDarkNavbar" aria-controls="offcanvasDarkNavbar">
+                        <span className="navbar-toggler-icon"></span>
+                    </button>
+                    <div className="offcanvas offcanvas-end text-bg-dark" tabIndex="-1" id="offcanvasDarkNavbar" aria-labelledby="offcanvasDarkNavbarLabel">
+                        <div className="offcanvas-header">
+                            <button type="button" className="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
+                        </div>
+                        <div className="offcanvas-body">
+                            <ul className="navbar-nav justify-content-end flex-grow-1 pe-3">
+                                <li className="nav-item">
+                                    <Link to="/" className="nav-link">Home</Link>
+                                </li>
+                                <li className="nav-item dropdown">
+                                    <a className="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
+                                        Enter
+                                    </a>
+                                    <ul className="dropdown-menu dropdown-menu-dark">
+                                        <li className='dropdown-li'><Link to="/login" className="nav-link">Login</Link></li>
+                                        <li className='dropdown-li'><Link to="/registration" className="nav-link">Registration</Link></li>
+                                    </ul>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </nav>
+        </header>
+    )
+}

+ 23 - 0
final_project/src/graphQl/getGQL.js

@@ -0,0 +1,23 @@
+const getGQL = (url) => (query, variables) => {
+    return fetch(url, {
+        method: "POST",
+        headers: {
+            "Content-Type": "application/json",
+            // 'Accept' : 'application/json',
+            ...(localStorage.authToken
+                ? { Authorization: "Bearer " + localStorage.authToken }
+                : {}),
+        },
+        body: JSON.stringify({ query, variables }),
+    })
+        .then((res) => res.json())
+        .then((data) => {
+            if (data.data) {
+                return Object.values(data.data)[0];
+            } else throw new Error(JSON.stringify(data.errors));
+        });
+}
+
+export const backendURL = "http://chat.ed.asmer.org.ua";
+export const gql = getGQL(backendURL + "/graphql");
+

+ 19 - 0
final_project/src/guard/AuthGuard.jsx

@@ -0,0 +1,19 @@
+/* import React from 'react'
+import { Route, Redirect } from 'react-router-dom'
+import {connect } from 'react-redux'
+
+
+const PrivateRoute = ({ component: Component, token, ...rest }) => (
+  
+  <Route {...rest} render={(props) => (  
+    token
+      ? <Component {...props} />
+      : <Redirect to={{
+        pathname: '/login',
+        state: { from: props.location }
+      }} />
+      
+  )} />
+)
+
+export default connect(null)(PrivateRoute) */

+ 5 - 0
final_project/src/helpers/jwt-decode.js

@@ -0,0 +1,5 @@
+export default function jwtDecode(token) {
+    try {
+        return JSON.parse(atob(token.split(".")[1]));
+    } catch (e) { }
+}

+ 3 - 0
final_project/src/history/index.js

@@ -0,0 +1,3 @@
+import createHistory from "history/createBrowserHistory";
+
+export const history = createHistory()

BIN
final_project/src/img/IMG_1920.JPG


BIN
final_project/src/img/IMG_1921.JPG


BIN
final_project/src/img/IMG_1922.JPG


BIN
final_project/src/img/bacground-chat.png


BIN
final_project/src/img/back.JPG


BIN
final_project/src/img/home.jpg


BIN
final_project/src/img/logo.jpg


BIN
final_project/src/img/sticker.webp


+ 13 - 0
final_project/src/index.css

@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}

+ 17 - 0
final_project/src/index.js

@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+import reportWebVitals from './reportWebVitals';
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render(
+  <App />
+/*   <React.StrictMode>
+    
+  </React.StrictMode> */
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();

File diff suppressed because it is too large
+ 1 - 0
final_project/src/logo.svg


+ 48 - 0
final_project/src/pages/AbouteMePage.jsx

@@ -0,0 +1,48 @@
+import React, { useState, useEffect } from 'react';
+import '../App.css'
+import store from "../store/index"
+import { backendURL } from '../graphQl/getGQL';
+export const PageAboutMe = ({ match: { params: { _id } }, getData, userName, avatar, chats }) => {
+    const [avatarImg, setAvatarImg] = useState(null)
+
+    useEffect(() => {
+        getData(_id)
+        const formElement = document.getElementById('form');
+        const photoInput = document.getElementById('media');
+        console.log('ava', avatar);
+        if (formElement && photoInput) {
+
+            photoInput.onchange = async () => {
+
+                fetch(`${backendURL}/upload`, {
+                    method: "POST",
+                    headers: localStorage.authToken ? { Authorization: 'Bearer ' + localStorage.authToken } : {},
+                    body: new FormData(formElement)
+                }).then(res => res.json()).then(json => {
+                    setAvatarImg(`${backendURL}/${json.url}`)
+                    console.log(avatarImg);
+                    console.log('UPLOAD RESULT', json)
+                })
+
+
+            }
+        }
+    }, [_id])
+
+
+    return (
+        <>
+            <h2>{userName}</h2>
+            {avatarImg && <img src={avatarImg} className='ava' alt="avatar" />}
+
+
+            <form action="/upload" method="post" enctype="multipart/form-data" id='form'>
+                <input type="file" name="media" id='media' />
+            </form>
+
+
+
+        </>
+    )
+}
+

+ 127 - 0
final_project/src/pages/ChatInfoPage.jsx

@@ -0,0 +1,127 @@
+import React, { useState, useEffect, useQuery } from 'react';
+import { Link, useParams } from 'react-router-dom';
+import io from 'socket.io-client';
+
+// 632b86aa55e76f7ddb1eae29
+
+import { history } from '../history/';
+import '../App.css';
+import store from '../store';
+import { actionAboutMe } from '../store/actions/action-aboutMe'
+import backgroundChat from '../img/back.JPG'
+import { useRef } from 'react';
+import { actionSearchUser } from '../store/actions/action-search-user';
+
+
+export const ChatInfoPage = ({ us, onSearchUser, match: { params: { _id } }, chats, getData, userName }) => {
+    const [chatList, setChatList] = useState(chats || [])
+    const [messageList, setMessageList] = useState([])
+    const [chatInfo, setChatInfo] = useState([])
+    const [searchUser, setSearchUser] = useState("")
+    const [userAdd, setUserAdd] = useState("")
+
+    const SearchUs = ( _id) => {
+        
+        onSearchUser(searchUser)
+        setSearchUser('')
+        const stD = store.dispatch(onSearchUser())
+        const userId = us
+        console.log('1', stD)
+        console.log('2', userId)
+        console.log(store.getState())
+        
+    
+    }
+    
+
+    useEffect(() => {
+        if (chats) {
+            const activeChatId = window.location.pathname.split('/')[2]
+            let activeChat = chats.filter((item) => item._id === activeChatId);
+            console.log('mem', activeChat)
+
+            setChatInfo(activeChat[0].members)
+        }
+    }, [chats])
+
+    useEffect(() => {
+
+        async function getDataFunc() {
+            const res = await getData(_id)
+
+            setChatList(res.chats)
+            setMessageList(res.chats?.message)
+            setChatInfo(res.chats?.members)
+        }
+
+        getDataFunc()
+    }, [_id])
+
+    function handleClick(_id) {
+        const filteredChatList = chatList.filter((item) => item._id === _id)
+        console.log(filteredChatList)
+        setMessageList(filteredChatList[0].messages)
+    }
+
+    function chatInfoClick(_id) {
+        const filteredChatList = chatList.filter((item) => item._id === _id)
+        console.log(filteredChatList)
+        setChatInfo(filteredChatList[0].members)
+    }
+
+    return (
+        <>
+            <div className='chatPage'>
+                <aside>
+                    <div className="row">
+                        <div className="col-12">
+                            <div id="list-example" className="list-group">
+                                <a href="/create-chat" className='linkToCreate'><h1>Создать чат</h1></a>
+                                <ul className='chatAside'>{chatList.map((item, index) =>
+                                    <a className="list-group-item list-group-item-action">
+                                        <Link to={`${item._id}/info`}> <h3 onClick={() => chatInfoClick(item._id)}>Инфо </h3> </Link>
+                                        <Link to={`/chat/${item._id}`}></Link>
+                                        <Link to={`/chat/${item._id}`}>
+                                            <li onClick={() => handleClick(item._id)} className='chatLi' key={index}>
+                                                <h3>Chat: {item.title}</h3>
+                                                {/* {item.lastMessage ? <h6>Last Message: {item.lastMessage.text}</h6> : []} */}
+                                            </li>
+                                        </Link>
+                                    </a>
+                                )}
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </aside>
+                <main className='chatMain'>
+                    <h1>Members Add</h1>
+                    <input
+                        type="text"
+                        value={searchUser}
+                        onChange={(e) => setSearchUser(e.target.value)}
+                        className="form-control"
+                        id="exampleInputEmail1"
+                        aria-describedby="emailHelp" />
+                    <div className="mb-3">
+                        <button type="submit" className="btn btn-primary" onClick={SearchUs}>Add</button>
+                    </div>
+
+                    <div className="membersChat">
+                        <h1> Members: </h1>
+
+                        {chatInfo  /* chatInfo.length > 0 */ &&
+                            <ul>{chatInfo.map((item2, index) =>
+                                <li key={index}>
+                                    <h1><p>{item2.login}</p></h1>
+                                </li>
+
+                            )}
+                            </ul>
+                        }
+                    </div>
+                </main>
+            </div>
+        </>
+    )
+}

+ 77 - 0
final_project/src/pages/ChatPage.jsx

@@ -0,0 +1,77 @@
+import React, { useState, useEffect } from 'react';
+import { Link, useParams } from 'react-router-dom';
+import { history } from '../history/';
+import '../App.css';
+import store from '../store';
+import { actionAboutMe } from '../store/actions/action-aboutMe'
+import backgroundChat from '../img/back.JPG'
+import { actionChatOne } from '../store/reducers/chat-reducer';
+
+
+
+export const ChatPage = ({ onCreateMsg, match: { params: { _id } }, chats, getData, userName }) => {
+    const [chatList, setChatList] = useState(chats || [])
+    const [messageList, setMessageList] = useState([])
+    const [msgAdd, setMsgAdd] = useState("")
+
+    const CreationMsg = () => {
+        onCreateMsg(msgAdd)
+        setMsgAdd('')
+    }
+
+
+    useEffect(() => {
+
+        async function getDataFunc() {
+            const res = await getData(_id)
+
+            setChatList(res.chats)
+            setMessageList(res.chats?.message)
+        }
+
+        getDataFunc()
+    }, [_id])
+
+    function handleClick(_id) {
+        const filteredChatList = chatList.filter((item) => item._id === _id)
+        console.log(filteredChatList)
+        setMessageList(filteredChatList[0].messages)
+        
+    }
+
+
+
+    return (
+        <>
+            <div className='chatPage'>
+                <aside>
+                    <div className="row">
+                        <div className="col-12">
+                            <div id="list-example" className="list-group">
+                                <a href="/create-chat" className='linkToCreate'><h1>Создать чат</h1></a>
+                                <ul className='chatAside'>{chatList.map((item, index) =>
+                                    <a className="list-group-item list-group-item-action">
+                                        <Link to={`/chat/${item._id}`}>
+                                            <li onClick={() => handleClick(item._id)} className='chatLi' key={index}>
+                                                <h3>Chat: {item.title}</h3>
+                                                {/* {item.lastMessage ? <h6>Last Message: {item.lastMessage.text}</h6> : []} */}
+                                            </li>
+                                        </Link>
+                                    </a>
+                                )}
+                                </ul>
+
+
+                            </div>
+                        </div>
+                    </div>
+                </aside>
+                <main className='chatMain'>
+                    <div className='chatPrewiew'>
+                        <h4>select a chat to start messaging</h4>
+                    </div>
+                </main>
+            </div>
+        </>
+    )
+}

+ 66 - 0
final_project/src/pages/CreateChatPage.jsx

@@ -0,0 +1,66 @@
+import { useState } from 'react';
+import {  Link } from 'react-router-dom';
+import store from '../store';
+import { history } from '../App';
+import '../App.css';
+import { actionSearchUser } from '../store/actions/action-search-user';
+
+
+export const CreateChatPage = ({ onCreate, us, onSearchUser }) => {
+    const [titleAdd, setTitleAdd] = useState("")
+    const [membersAdd, setMembersAdd] = useState("")
+    const [searchUser, setSearchUser] = useState("")
+
+    const SearchUs = ( _id) => {
+        
+        onSearchUser(membersAdd)
+        
+        const stD = store.dispatch(actionSearchUser())
+        const userId = us
+        console.log('1', stD)
+        console.log('2', userId)
+    }
+
+    const Creation = () => {
+        SearchUs()
+        onCreate(titleAdd, membersAdd)
+        setTitleAdd('')
+        setMembersAdd('')
+        
+    }
+
+    
+
+    return (
+        <div className="formBlock">
+
+        <h1>Создать чат</h1>
+        <div className="mb-3">
+            <label htmlFor="exampleInputEmail1" className="form-label">Enter Title</label>
+            <input
+                type="text"
+                value={titleAdd}
+                onChange={(e) => setTitleAdd(e.target.value)}
+                className="form-control"
+                id="exampleInputEmail1"
+                aria-describedby="emailHelp" />
+        </div>
+        <div className="mb-3">
+            <label htmlFor="exampleInputEmail1" className="form-label">Enter UserName</label>
+            <input
+                type="text"
+                value={membersAdd}
+                onChange={(e) => setMembersAdd(e.target.value)}
+                className="form-control"
+                id="exampleInputEmail1"
+                aria-describedby="emailHelp" />
+        </div>
+
+        <div className="mb-3">
+            <button type="submit" className="btn btn-primary" onClick={Creation}>Создать</button>
+        </div>
+
+        <Link to="/chat">Вернуться к чатам</Link>
+    </div>
+    )
+}

+ 17 - 0
final_project/src/pages/HomePage.jsx

@@ -0,0 +1,17 @@
+import logo from './../img/logo.jpg'
+
+export const HomePage = () => {
+    // const stylePage = {
+        
+    //     backgroundImage: `url(${logo}) no-repeat center center fixed`
+        
+    // }
+
+    return(
+        <main className='HomeMain' >
+            <div className='divHome'/*  style={stylePage} */>
+                <img src={logo} alt="image description" className='home-logo'/>
+            </div>
+        </main>
+    )
+}

+ 60 - 0
final_project/src/pages/LoginPage.jsx

@@ -0,0 +1,60 @@
+import { useState } from 'react';
+import {  Link } from 'react-router-dom';
+import '../App.css';
+import {history} from '../history'
+import store from '../store';
+import actionChatList from '../store/reducers/chat-reducer'
+
+export const LoginPage = ({ onLogin, outLogin }) => {
+    const [login, setLogin] = useState("");
+    const [password, setPassword] = useState("");
+
+
+    const Authorization = () => {
+        if(localStorage.authToken){
+            history.push('/chat')
+        }
+        onLogin(login, password);
+        setLogin('');
+        setPassword('');
+        
+    };
+
+    const OutAuthorization = () => {
+        outLogin();
+    }
+
+    return (
+        <div className="formBlock">
+            
+                <h1>Вход</h1>
+                <div className="mb-3">
+                    <label htmlFor="exampleInputEmail1" className="form-label">Enter Login</label>
+                    <input 
+                        type="text"
+                        value={login} 
+                        onChange={(e) => setLogin(e.target.value)}
+                        className="form-control" 
+                        id="exampleInputEmail1" 
+                        aria-describedby="emailHelp" />
+                </div>
+                <div className="mb-3">
+                    <label htmlFor="exampleInputPassword1" className="form-label">Enter Password</label>
+                    <input 
+                        type="password"
+                        value={password}
+                        onChange={(e) => setPassword(e.target.value)} 
+                        className="form-control" 
+                        id="exampleInputPassword1" />
+                </div>
+                <div className="mb-3">
+                    <button type="submit" className="btn btn-primary" onClick={Authorization}>Войти</button>
+                </div>
+                
+                {/* <button onClick={OutAuthorization}>Log Out</button> */}
+                <Link to="/registration">Зарегистрироваться</Link>
+
+        </div>
+
+    );
+};

+ 4 - 0
final_project/src/pages/MainPage.jsx

@@ -0,0 +1,4 @@
+import { useState } from 'react';
+import {  Link } from 'react-router-dom';
+import { history } from '../App';
+import '../App.css';

+ 126 - 0
final_project/src/pages/OpenChatPage.jsx

@@ -0,0 +1,126 @@
+import React, { useState, useEffect } from 'react';
+import { Link, useParams } from 'react-router-dom';
+import io from 'socket.io-client';
+
+
+import { history } from '../history/';
+import '../App.css';
+import store from '../store';
+import { actionAboutMe } from '../store/actions/action-aboutMe'
+import backgroundChat from '../img/back.JPG'
+import { useRef } from 'react';
+
+
+export const OpenChatPage = ({ onCreateMsg, match: { params: { _id } }, chats, getData, userName }) => {
+    const [chatList, setChatList] = useState(chats || [])
+    const [messageList, setMessageList] = useState([])
+    const [chatInfo, setChatInfo] = useState([])
+    const [msgAdd, setMsgAdd] = useState("")
+
+    useEffect(() => {
+        if (chats) {
+            const activeChatId = window.location.pathname.split('/')[2]
+            let activeChat = chats.filter((item) => item._id === activeChatId);
+            console.log('sadasdasd', activeChat)
+
+            setMessageList(activeChat[0].messages)
+        }
+    }, [chats])
+
+    useEffect(() => {
+        if (chats) {
+            const activeChatId = window.location.pathname.split('/')[2]
+            let activeChat = chats.filter((item) => item._id === activeChatId);
+            console.log('mem', activeChat)
+
+            setChatInfo(activeChat[0].members)
+        }
+    }, [chats])
+
+    const CreationMsg = () => {
+        onCreateMsg(msgAdd)
+        setMsgAdd('')
+    }
+
+
+    useEffect(() => {
+
+        async function getDataFunc() {
+            const res = await getData(_id)
+
+            setChatList(res.chats)
+            setMessageList(res.chats?.message)
+            setChatInfo(res.chats?.members)
+        }
+
+        getDataFunc()
+    }, [_id])
+
+    function handleClick(_id) {
+        const filteredChatList = chatList.filter((item) => item._id === _id)
+        console.log(filteredChatList)
+        setMessageList(filteredChatList[0].messages)
+    }
+
+    function chatInfoClick(_id) {
+        const filteredChatList = chatList.filter((item) => item._id === _id)
+        console.log(filteredChatList)
+        setChatInfo(filteredChatList[0].members)
+    }
+
+
+    return (
+        <>
+            <div className='chatPage'>
+                <aside>
+                    <div className="row">
+                        <div className="col-12">
+                            <div id="list-example" className="list-group">
+                                <a href="/create-chat" className='linkToCreate'><h1>Создать чат</h1></a>
+                                <ul className='chatAside'>{chatList.map((item, index) =>
+
+                                    <a className="list-group-item list-group-item-action">
+                                        <Link to={`/chat/${item._id}/info`}> <h3 onClick={() => chatInfoClick(item._id)}>Инфо </h3> </Link>
+                                        <Link to={`/chat/${item._id}`}>
+                                            <li onClick={() => handleClick(item._id)} className='chatLi' key={index}>
+                                                <h3>Chat: {item.title}</h3>
+                                                {/* {item.lastMessage ? <h6>Last Message: {item.lastMessage.text}</h6> : []} */}
+                                            </li>
+                                        </Link>
+                                    </a>
+                                )}
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </aside>
+                <main className='chatMain'>
+                    <div className="mb-3 sendMsg">
+                        <input
+                            type="text"
+                            value={msgAdd}
+                            onChange={(e) => setMsgAdd(e.target.value)}
+                            className="form-control"
+                            id="exampleInputEmail1"
+                            aria-describedby="emailHelp" />
+                        <button type="submit" className="btn btn-primary" onClick={CreationMsg}>Отправить</button>
+
+                    </div>
+
+                    {messageList && messageList.length > 0 &&
+
+                        <ul className='msgUl'>{messageList.map((item1, index) =>
+
+                            <li className='msgLi' key={index}>
+                                <h4><p className='msgOwner'>{item1.owner.login}</p> <p>{item1.text}</p></h4>
+                            </li>
+                        )}
+
+                        </ul>
+
+                    }
+                </main>
+            </div>
+        </>
+    )
+}

+ 47 - 0
final_project/src/pages/RegisterPage.jsx

@@ -0,0 +1,47 @@
+import { useState } from 'react';
+import {  Link } from 'react-router-dom';
+import { history } from '../App';
+import '../App.css';
+
+export const RegisterPage = ({ onRegister }) => {
+    const [loginReg, setLoginReg] = useState("");
+    const [passwordReg, setPasswordReg] = useState("");
+
+    const Registration = () => {
+        onRegister(loginReg, passwordReg)
+        setLoginReg('')
+        setPasswordReg('')
+    }
+
+    return (
+        <div className="formBlock">
+
+            <h1>Регистрация</h1>
+            <div className="mb-3">
+                <label htmlFor="exampleInputEmail1" className="form-label">Enter Login</label>
+                <input
+                    type="text"
+                    value={loginReg}
+                    onChange={(e) => setLoginReg(e.target.value)}
+                    className="form-control"
+                    id="exampleInputEmail1"
+                    aria-describedby="emailHelp" />
+            </div>
+            <div className="mb-3">
+                <label htmlFor="exampleInputPassword1" className="form-label">Enter Password</label>
+                <input
+                    type="password"
+                    value={passwordReg}
+                    onChange={(e) => setPasswordReg(e.target.value)}
+                    className="form-control"
+                    id="exampleInputPassword1" />
+            </div>
+
+            <div className="mb-3">
+                <button type="submit" className="btn btn-primary" onClick={Registration}>Зарегистрироваться</button>
+            </div>
+
+            <Link to="/login">Я уже зарегистрирован</Link>
+        </div>
+    )
+}

+ 13 - 0
final_project/src/reportWebVitals.js

@@ -0,0 +1,13 @@
+const reportWebVitals = onPerfEntry => {
+  if (onPerfEntry && onPerfEntry instanceof Function) {
+    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+      getCLS(onPerfEntry);
+      getFID(onPerfEntry);
+      getFCP(onPerfEntry);
+      getLCP(onPerfEntry);
+      getTTFB(onPerfEntry);
+    });
+  }
+};
+
+export default reportWebVitals;

+ 5 - 0
final_project/src/setupTests.js

@@ -0,0 +1,5 @@
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';

+ 52 - 0
final_project/src/store/actions/action-aboutMe.js

@@ -0,0 +1,52 @@
+import actionPromise from "./action-promise";
+import { gql } from '../../graphQl/getGQL'
+import jwtDecode from '../../helpers/jwt-decode'
+
+
+export const actionAboutMe = () => {
+    let _id = jwtDecode(localStorage.authToken).sub.id
+    return (
+        actionPromise('userData', gql(`
+            query($userId: String!) {
+                UserFindOne(query: $userId){
+                    login, _id, avatar {_id, url, originalFileName}, chats{
+                        members {
+                            _id
+                            createdAt
+                            login
+                            nick
+                          }
+                        messages {
+                            _id
+                            createdAt
+                            text
+                                    owner {
+                                      _id
+                                      createdAt
+                                      login
+                                      nick
+                                    }
+                            media {
+                              _id
+                              createdAt
+                              text
+                              url
+                              originalFileName
+                              type
+                            }
+                          }
+                        _id
+                        createdAt
+                        title
+                        lastMessage {
+                            _id
+                            createdAt
+                            text
+                        }
+                    }
+                }
+            }
+        `, { userId: JSON.stringify([{ _id }]) }))
+    )
+}
+

+ 33 - 0
final_project/src/store/actions/action-chats-promise.js

@@ -0,0 +1,33 @@
+export default function actionChatsPromise(name, promise) {
+    const actionPending = (name) => ({
+        type: "MSG",
+        status: "PENDING",
+        name,
+    });
+    
+    const actionFulfilled = (name, payload) => ({
+        type: "MSG",
+        status: "FULFILLED",
+        name,
+        payload,
+    });
+    
+    const actionRejected = (name, error) => ({
+        type: "MSG",
+        status: "REJECTED",
+        name,
+        error,
+    });
+
+    return async dispatch => {
+        try {
+            dispatch(actionPending())
+            let payload = await promise
+            dispatch(actionFulfilled(name, payload));
+            return payload
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }
+}

+ 29 - 0
final_project/src/store/actions/action-chats.js

@@ -0,0 +1,29 @@
+import actionPromise from "./action-promise";
+import {gql} from '../../graphQl/getGQL'
+import jwtDecode from '../../helpers/jwt-decode'
+
+/* export const actionAboutMe = () => {
+    let _id = jwtDecode(localStorage.authToken).sub.id
+    return (
+        actionPromise('userData', gql(`
+            query($userId: String!) {
+                UserFindOne(query: $userId){
+                    login, _id, avatar {_id, url, originalFileName}
+                }
+            }
+        `, { userId: JSON.stringify([{ _id }]) }))
+    )
+} */
+
+/* export const actionChats = () => {
+    let _id = jwtDecode(localStorage.authToken).sub.id
+    return (
+        actionPromise('chats', gql(`
+            query($userId: String!) {
+                UserFindOne(query: $userId){
+                    login, _id, avatar {_id, url, originalFileName}
+                }
+            }
+        `, { userId: JSON.stringify([{ _id }]) }))
+    )
+} */

+ 39 - 0
final_project/src/store/actions/action-create-chat.js

@@ -0,0 +1,39 @@
+import actionPromise from "./action-promise"
+import { gql } from '../../graphQl/getGQL'
+
+export const actionCreateChat = (title, members, chatId, membersId) =>
+   actionPromise(
+      'updateChat',
+      gql(
+         `mutation updateChat($chat:ChatInput) {
+      ChatUpsert(chat:$chat) {
+         _id
+         title
+         avatar {
+            _id
+            url
+         }
+         owner {
+            _id
+            login
+            avatar {
+               _id
+               url
+            }
+         }
+         members {
+            _id
+            login
+            nick
+            avatar {
+               _id
+               url
+            }
+         }
+         lastModified    
+      }
+   }`,
+         // { chat: { _id: chatId, title, members }, members: {_id: userId} }
+         { chat: { title: title, members: {_id: members}} }
+      )
+   )

+ 39 - 0
final_project/src/store/actions/action-create-msg.js

@@ -0,0 +1,39 @@
+import actionPromise from "./action-promise"
+import { gql } from '../../graphQl/getGQL'
+import { useParams } from "react-router-dom"
+
+
+
+export const actionCreateMessage = (text, _id, chats) =>  {
+    const windowId = window.location.pathname
+    const windowSplit = windowId.split('/')
+    const chatId = windowSplit[2]
+
+    return (
+        actionPromise(
+            'updateMsg',
+            gql(
+                `mutation newMsg($text: String, $id: ID) {
+                    MessageUpsert(message: {chat: {_id: $id} text: $text}){
+                        _id owner {
+                        _id
+                        createdAt
+                        login
+                        nick
+                        } chat {
+                        _id
+                        createdAt
+                        lastModified
+                        title
+                        } text media {
+                        _id
+                        createdAt
+                        text
+                        url
+                        originalFileName
+                        type
+                        } createdAt
+                    }
+                }`, { id: chatId, text: text }))
+    )
+}

+ 51 - 0
final_project/src/store/actions/action-login-promise.js

@@ -0,0 +1,51 @@
+import {history} from '../../history'
+import actionPromise from "./action-promise";
+import {gql} from '../../graphQl/getGQL'
+import {actionAboutMe} from '../actions/action-aboutMe'
+import {socket} from './../'
+
+// import io from 'socket.io-client';
+
+// const socket = io();
+
+
+export const actionAuthLogin = (token) => ({ type: "AUTH_LOGIN", token });
+
+export const actionFullLogin = (login, password) => async (dispatch) => {
+    let token = await dispatch(
+        actionPromise(
+            "login",
+            gql(
+                `query log($login:String!, $password:String!){
+                    login(login: $login, password: $password)
+                  }`,
+                { login: login, password: password }
+            )
+        )
+    );
+    if (token) {
+        await dispatch(actionAuthLogin(token));
+        await dispatch(actionAboutMe())
+        history.push('/chat')
+        
+        
+        // const socket = socketIO("ws://chat.ed.asmer.org.ua");
+        // socket.on('msg', data => {
+        //     console.log(JSON.stringify(data));
+        //     alert(data);
+        // });
+        // socket.on('chat', data => {
+        //     console.log(JSON.stringify(data));
+        //     alert(data);
+        // })
+        // socket.on('chat_left', data => {
+        //      console.log(JSON.stringify(data));
+        //      alert(data);
+        // })
+
+        // debugger
+    }
+    if (!token) {
+        history.push('/')
+    }
+};

+ 4 - 0
final_project/src/store/actions/action-logout.js

@@ -0,0 +1,4 @@
+export const actionAuthLogout = () => (dispatch) => {
+    dispatch({ type: "AUTH_LOGOUT" });
+    localStorage.removeItem("authToken");
+};

+ 33 - 0
final_project/src/store/actions/action-promise.js

@@ -0,0 +1,33 @@
+export default function actionPromise(name, promise) {
+    const actionPending = (name) => ({
+        type: "PROMISE",
+        status: "PENDING",
+        name,
+    });
+    
+    const actionFulfilled = (name, payload) => ({
+        type: "PROMISE",
+        status: "FULFILLED",
+        name,
+        payload,
+    });
+    
+    const actionRejected = (name, error) => ({
+        type: "PROMISE",
+        status: "REJECTED",
+        name,
+        error,
+    });
+
+    return async dispatch => {
+        try {
+            dispatch(actionPending())
+            let payload = await promise
+            dispatch(actionFulfilled(name, payload));
+            return payload
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }
+}

+ 17 - 0
final_project/src/store/actions/action-register-promise.js

@@ -0,0 +1,17 @@
+import actionPromise from "./action-promise"
+import {gql} from '../../graphQl/getGQL'
+
+export const actionRegister = (login, password) => async (dispatch) => {
+    actionPromise(
+        "register",
+        gql(
+            `mutation register($login: String, $password: String) {
+                    UserUpsert(user: {login: $login, password: $password}) {
+                    _id
+                    login
+                    }
+                }`,
+            { login: login, password: password }
+        )
+    )
+}

+ 27 - 0
final_project/src/store/actions/action-search-user.js

@@ -0,0 +1,27 @@
+import actionPromise from "./action-promise";
+import { gql } from '../../graphQl/getGQL'
+import jwtDecode from '../../helpers/jwt-decode'
+
+// export const actionSearchUser = (login, _id) => async (dispatch) => {
+//     // let _id = jwtDecode(localStorage.authToken).sub.id
+//     return(
+//         actionPromise('searchUs', gql(`
+//         query($userLogin: String!){
+//             UserFindOne(query: $userLogin){
+//                 login, _id
+//             }
+//         }`, { userLogin: JSON.stringify([{ login }]) }))
+//     )
+// }
+
+export const actionSearchUser = (login) => {
+    let _id = jwtDecode(localStorage.authToken).sub.id
+    return(
+        actionPromise('searchUs', gql(`
+        query($userLogin: String!){
+            UserFindOne(query: $userLogin){
+                login, _id
+            }
+        }`, { userLogin: JSON.stringify([{ login }]) }))
+    )
+}

+ 1 - 0
final_project/src/store/actions/actionMsgOn.js

@@ -0,0 +1 @@
+export const actionMsgOne = (msg) => ({ type: "MSG", msg });

+ 60 - 0
final_project/src/store/index.js

@@ -0,0 +1,60 @@
+import { createStore, applyMiddleware } from 'redux'
+import thunk from 'redux-thunk';
+import promiseReducer from './reducers/promise-reducer'
+import { combineReducers  } from "redux";
+import authReducer from './reducers/auth-reducer'
+import {history} from '../history'
+import chatsReducer from './reducers/chat-reducer';
+import {actionAuthLogout} from './actions/action-logout'
+import {actionCreateMessage} from './actions/action-create-msg'
+import {actionMsgOne} from './actions/actionMsgOn'
+import { actionAboutMe } from './actions/action-aboutMe';
+import { OpenChatPage } from '../pages/OpenChatPage';
+
+
+const store = createStore(
+    combineReducers({
+        auth: authReducer,
+        promise: promiseReducer,
+        chat: chatsReducer
+    }),
+    applyMiddleware(thunk)
+
+    
+);
+
+export const socket = window.io("ws://chat.ed.asmer.org.ua");
+
+socket.on("jwt_ok", (data) => console.log(data));
+socket.on("jwt_fail", (error) => {
+    console.log(error);
+});
+
+socket.on("msg", (msg) => {
+    console.log("пришло смс");
+    console.log(msg)
+
+    store.dispatch(actionMsgOne(msg))
+    
+});
+
+socket.on("chat", (chat) => {
+    console.log("нас добавили в чат");
+    const state = store.getState();
+    socket.emit("jwt", state.auth.token);
+});
+
+socket.on("chat_left", (chat) => {
+    console.log("нас выкинули из чата");
+});
+
+
+store.subscribe(() => {
+    const authToken = store.getState().auth?.token;
+    if (!authToken) {
+        history.push('/')
+    }
+
+console.log(store.getState())})
+
+export default store

+ 22 - 0
final_project/src/store/reducers/auth-reducer.js

@@ -0,0 +1,22 @@
+import jwtDecode from '../../helpers/jwt-decode'
+
+export default function authReducer(state, { type, token }) {
+    if (state === undefined) {
+        if (localStorage.authToken) {
+            type = "AUTH_LOGIN";
+            token = localStorage.authToken;
+        }
+    }
+    if (type === "AUTH_LOGIN") {
+        let payload = jwtDecode(token);
+        if (payload) {
+            localStorage.authToken = token;
+            return { token, payload };
+        }
+    }
+    if (type === "AUTH_LOGOUT") {
+        localStorage.removeItem("authToken");
+        return {};
+    }
+    return state || {};
+}

+ 73 - 0
final_project/src/store/reducers/chat-reducer.js

@@ -0,0 +1,73 @@
+import jwtDecode from '../../helpers/jwt-decode'
+import store from '..';
+
+export default function chatReducer(state = {}, { type, name, status, payload, error, msg, members, user }) {
+    if (!state) {
+        return {};
+    }
+    if (type === "PROMISE") {
+
+        return {
+            ...state,
+            [name]: { status, payload, error },
+        };
+    }
+    if (type === "MSG") {
+        const chat = state.chat?.userData.payload.chats.find(i => i._id === msg.chat._id)
+
+        console.log('state', state)
+        console.log('mssg', msg)
+
+        const newState = {
+            ...state,
+            userData: {
+                ...state.userData,
+                payload: {
+                    ...state.userData.payload,
+                    chats: state.userData.payload.chats.map(item => {
+                        if (item._id === msg.chat._id) {
+                            item.messages = [...item.messages, {
+                                createdAt: msg.createdAt,
+                                media: null,
+                                owner: msg.owner,
+                                text: msg.text,
+                                _id: msg._id
+                            }]
+                            return item
+                        } else return item
+                    })
+
+                }
+
+            },
+        };
+
+        return newState;
+    }
+    if (type === "CHAT") {
+            if (payload) {
+                const oldChats = { ...state };
+
+                for (const chat of payload) {
+                    const oldChat = oldChats[chat._id];
+
+                    if (!oldChat) {
+                        oldChats[chat._id] = { ...chat };
+                    } 
+                }
+            }
+            return state;
+    }
+    if (type === "ADD_USER_CHAT") {
+        const chat = state.chat?.userData.payload.chats
+        return {
+            ...state,
+            // [chat.members]: { members: members + state[] }
+        }
+    }
+    if (type === "CHAT_CLEAR") {
+        return {}
+    }
+
+    return state;
+}

+ 13 - 0
final_project/src/store/reducers/local-store-reducer.js

@@ -0,0 +1,13 @@
+function localStoreReducer(reducer, localStorageKey) {
+    function localStoredReducer(state, action) {
+        if (state === undefined) {
+            try {
+                return JSON.parse(localStorage[localStorageKey]);
+            } catch (e) { }
+        }
+        const newState = reducer(state, action);
+        localStorage[localStorageKey] = JSON.stringify(newState);
+        return newState;
+    }
+    return localStoredReducer;
+}

+ 27 - 0
final_project/src/store/reducers/promise-reducer.js

@@ -0,0 +1,27 @@
+export default function promiseReducer(state = {}, { type, name, status, payload, error }) {
+    if (type === "PROMISE") {
+        return {
+            ...state,
+            [name]: { status, payload, error },
+        };
+    }
+    // if (type === "MSG") {
+        
+
+    //     if (payload && payload.length > 0) {
+    //         const chatId = payload[0]?.chat?._id;
+
+    //         const newState = {
+    //             ...state,
+    //             [chatId]: {
+    //                 ...state[chatId]
+    //             },
+    //         };
+
+    //         return newState;
+    //     }
+    //     return state;
+    // }
+    
+    return state;
+}