Browse Source

First upload of project

surstrommed 2 năm trước cách đây
mục cha
commit
c24a70e618

+ 71 - 0
package-lock.json

@@ -21,6 +21,7 @@
         "react": "^17.0.2",
         "react-bootstrap": "^2.1.0",
         "react-dom": "^17.0.2",
+        "react-dropzone": "^11.5.1",
         "react-redux": "^7.2.6",
         "react-router": "^5.2.0",
         "react-router-dom": "^5.2.0",
@@ -4575,6 +4576,14 @@
         "node": ">= 4.5.0"
       }
     },
+    "node_modules/attr-accept": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
+      "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/autoprefixer": {
       "version": "10.4.1",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.1.tgz",
@@ -7661,6 +7670,22 @@
         "webpack": "^4.0.0 || ^5.0.0"
       }
     },
+    "node_modules/file-selector": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
+      "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
+      "dependencies": {
+        "tslib": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/file-selector/node_modules/tslib": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+      "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+    },
     "node_modules/filelist": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz",
@@ -13768,6 +13793,22 @@
         "react": "17.0.2"
       }
     },
+    "node_modules/react-dropzone": {
+      "version": "11.5.1",
+      "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.5.1.tgz",
+      "integrity": "sha512-eNhttdq4ZDe3eKbXAe54Opt+sbtqmNK5NWTHf/l5d+1TdZqShJ8gMjBrya00qx5zkI//TYxRhu1d9pemTgaWwg==",
+      "dependencies": {
+        "attr-accept": "^2.2.1",
+        "file-selector": "^0.2.2",
+        "prop-types": "^15.7.2"
+      },
+      "engines": {
+        "node": ">= 10"
+      },
+      "peerDependencies": {
+        "react": ">= 16.8"
+      }
+    },
     "node_modules/react-error-overlay": {
       "version": "6.0.10",
       "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
@@ -20197,6 +20238,11 @@
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
       "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
     },
+    "attr-accept": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
+      "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
+    },
     "autoprefixer": {
       "version": "10.4.1",
       "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.1.tgz",
@@ -22518,6 +22564,21 @@
         "schema-utils": "^3.0.0"
       }
     },
+    "file-selector": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
+      "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
+      "requires": {
+        "tslib": "^2.0.3"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.3.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        }
+      }
+    },
     "filelist": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz",
@@ -26778,6 +26839,16 @@
         "scheduler": "^0.20.2"
       }
     },
+    "react-dropzone": {
+      "version": "11.5.1",
+      "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.5.1.tgz",
+      "integrity": "sha512-eNhttdq4ZDe3eKbXAe54Opt+sbtqmNK5NWTHf/l5d+1TdZqShJ8gMjBrya00qx5zkI//TYxRhu1d9pemTgaWwg==",
+      "requires": {
+        "attr-accept": "^2.2.1",
+        "file-selector": "^0.2.2",
+        "prop-types": "^15.7.2"
+      }
+    },
     "react-error-overlay": {
       "version": "6.0.10",
       "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "react": "^17.0.2",
     "react-bootstrap": "^2.1.0",
     "react-dom": "^17.0.2",
+    "react-dropzone": "^11.5.1",
     "react-redux": "^7.2.6",
     "react-router": "^5.2.0",
     "react-router-dom": "^5.2.0",

+ 4 - 2
src/App.js

@@ -8,7 +8,7 @@ import { createBrowserHistory } from "history";
 import { promiseReducer, authReducer } from "./reducers/index";
 import { Sidebar } from "./components/Sidebar";
 import "./App.scss";
-
+import { actionAboutMe } from "./actions";
 export const history = createBrowserHistory();
 
 export const store = createStore(
@@ -19,7 +19,9 @@ export const store = createStore(
   applyMiddleware(thunk)
 );
 
-export const getState = () => store.getState();
+if (localStorage.authToken) {
+  store.dispatch(actionAboutMe());
+}
 
 store.subscribe(() => console.log(store.getState()));
 

+ 49 - 0
src/App.scss

@@ -6,6 +6,55 @@ body {
   font-family: "Catamaran", sans-serif;
 }
 
+.spoilerText {
+  float: left;
+}
+
+.Spoiler {
+  display: flex;
+  flex-wrap: wrap;
+
+  .header {
+    padding-left: 1.5em;
+    transition: opacity 0.15s linear;
+    -khtml-user-select: none;
+    user-select: none;
+    :hover {
+      text-decoration: underline;
+      cursor: pointer;
+    }
+  }
+  .content {
+    flex: 0 0 100%;
+  }
+}
+
+.spoilerArrow {
+  float: right;
+}
+
+.customBorder {
+  border-width: 1px;
+  border-style: dashed !important;
+}
+
+.customBrand {
+  color: white;
+  font-size: 16px;
+}
+
+.avatarProfile {
+  width: 30vh;
+  height: 30vh;
+}
+
+.avatarHeader {
+  vertical-align: middle;
+  margin-right: 2vh;
+  width: 40px;
+  height: 40px;
+}
+
 .MainContent {
   margin-left: 7%;
 }

+ 43 - 28
src/actions/index.js

@@ -1,5 +1,4 @@
-import { gql } from "../helpers";
-import { getState } from "./../App";
+import { backURL, gql } from "../helpers";
 
 export const actionPending = (name) => ({
   type: "PROMISE",
@@ -23,14 +22,6 @@ export const actionRejected = (name, error) => ({
 
 export const actionAuthLogin = (token) => ({ type: "AUTH_LOGIN", token });
 
-export const actionAboutMe = (id, login, nick, avatar) => ({
-  type: "ABOUT_ME",
-  id,
-  login,
-  nick,
-  avatar,
-});
-
 export const actionAuthLogout = () => ({ type: "AUTH_LOGOUT" });
 
 export const actionPromise = (name, promise) => async (dispatch) => {
@@ -85,19 +76,18 @@ export const actionFindUser = (_id) =>
     )
   );
 
-export const actionFullLogin =
-  (login, password) => async (dispatch, getState) => {
-    let token = await dispatch(actionLogin(login, password));
-    if (token) {
-      dispatch(actionAuthLogin(token));
-      const currentState = getState();
-      let id = currentState.auth.payload.sub.id;
-      let user = await dispatch(actionFindUser(id));
-      if (user._id) {
-        dispatch(actionAboutMe(user._id, user.login, user.nick, user.avatar));
-      }
-    }
-  };
+export const actionAboutMe = () => async (dispatch, getState) => {
+  let { id } = getState().auth.payload.sub;
+  await dispatch(actionFindUser(id));
+};
+
+export const actionFullLogin = (login, password) => async (dispatch) => {
+  let token = await dispatch(actionLogin(login, password));
+  if (token) {
+    await dispatch(actionAuthLogin(token));
+    await dispatch(actionAboutMe());
+  }
+};
 
 export const actionFullRegister = (login, password) => async (dispatch) => {
   let check = await dispatch(actionRegister(login, password));
@@ -138,7 +128,7 @@ export const actionFindUsers = () =>
     )
   );
 
-export const actionUserUpdate = (_id, nick) =>
+export const actionUserUpdate = ({ _id, login, password, nick, avatar }) =>
   actionPromise(
     "userUpdate",
     gql(
@@ -150,11 +140,36 @@ export const actionUserUpdate = (_id, nick) =>
   `,
       {
         user: {
-          _id: "61d45a16e9472933a6785f04",
-          login: "",
-          password: "",
-          nick: "",
+          _id,
+          login,
+          password,
+          nick,
+          avatar,
         },
       }
     )
   );
+
+const actionUploadPhoto = (file) => async (dispatch) => {
+  let fd = new FormData();
+  fd.append("photo", file);
+  await dispatch(
+    actionPromise(
+      "uploadPhoto",
+      fetch(`${backURL}/upload`, {
+        method: "POST",
+        headers: localStorage.authToken
+          ? { Authorization: "Bearer " + localStorage.authToken }
+          : {},
+        body: fd,
+      }).then((res) => res.json())
+    )
+  );
+};
+
+export const actionSetAvatar = (file) => async (dispatch, getState) => {
+  await dispatch(actionUploadPhoto(file));
+  let { _id } = getState().promise.uploadFile.payload;
+  let { id } = getState().auth.payload.sub;
+  await dispatch(actionUserUpdate({ _id: id, avatar: { _id } }));
+};

+ 29 - 0
src/components/Dropzone.js

@@ -0,0 +1,29 @@
+import React, { useCallback } from "react";
+import { useDropzone } from "react-dropzone";
+import { connect } from "react-redux";
+import { actionSetAvatar } from "../actions";
+
+const MyDropzone = ({ onload }) => {
+  const onDrop = useCallback(
+    (acceptedFiles) => {
+      onload(acceptedFiles[0]);
+    },
+    [onload]
+  );
+  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
+
+  return (
+    <div className="mt-2 text-center customBorder" {...getRootProps()}>
+      <input {...getInputProps()} />
+      {isDragActive ? (
+        <p>Поместите файлы сюда...</p>
+      ) : (
+        <p>Перетащите файлы сюда или нажмите на поле и выберите файлы</p>
+      )}
+    </div>
+  );
+};
+
+export const CMyDropzone = connect(null, {
+  onload: actionSetAvatar,
+})(MyDropzone);

+ 1 - 12
src/components/Header.js

@@ -1,18 +1,10 @@
-import { faMusic } from "@fortawesome/free-solid-svg-icons";
 import React from "react";
 import { Navbar, Container, Nav, Button } from "react-bootstrap";
-import { Link } from "react-router-dom";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faArrowLeft, faArrowRight } from "@fortawesome/free-solid-svg-icons";
 import { CAuth } from "../pages/Auth";
 import { history } from "./../App";
 
-const Logo = ({ logo }) => (
-  <Link to="/" className="navbar-brand">
-    <FontAwesomeIcon icon={faMusic} color="#0B5ED7" /> Navy Web Player
-  </Link>
-);
-
 export const Header = () => {
   return (
     <Navbar
@@ -23,12 +15,9 @@ export const Header = () => {
       variant="dark"
     >
       <Container>
-        <Navbar.Brand>
-          <Logo logo={faMusic} />
-        </Navbar.Brand>
         <Navbar.Toggle aria-controls="responsive-navbar-nav" />
         <Navbar.Collapse id="responsive-navbar-nav">
-          <Nav className="me-auto">
+          <Nav className="me-auto d-flex mx-auto">
             <Button className="btn-circle" onClick={() => history.goBack()}>
               <FontAwesomeIcon icon={faArrowLeft} />
             </Button>

+ 20 - 0
src/components/Loader.js

@@ -0,0 +1,20 @@
+import { Button, Spinner } from "react-bootstrap";
+
+export const Loader = () => {
+  return (
+    <Button
+      className="d-block mx-auto mt-5 text-center"
+      variant="dark"
+      disabled
+    >
+      <Spinner
+        as="span"
+        animation="grow"
+        size="sm"
+        role="status"
+        aria-hidden="true"
+      />
+      Loading...
+    </Button>
+  );
+};

+ 11 - 0
src/components/Logo.js

@@ -0,0 +1,11 @@
+import { faMusic } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Link } from "react-router-dom";
+
+const LogoIcon = () => <FontAwesomeIcon icon={faMusic} color="#0B5ED7" />;
+
+export const Logo = () => (
+  <Link to="/" className="navbar-brand customBrand">
+    <LogoIcon /> Navy Web Player
+  </Link>
+);

+ 3 - 8
src/components/Sidebar.js

@@ -6,8 +6,8 @@ import {
   CDBSidebarMenu,
   CDBSidebarMenuItem,
 } from "cdbreact";
-import { Link } from "react-router-dom";
 import { NavLink } from "react-router-dom";
+import { Logo } from "./Logo";
 
 export const Sidebar = () => {
   return (
@@ -16,18 +16,13 @@ export const Sidebar = () => {
         display: "flex",
         height: "100vh",
         overflow: "scroll initial",
+        top: 0,
         position: "fixed",
       }}
     >
       <CDBSidebar backgroundColor="#212529">
         <CDBSidebarHeader prefix={<i className="fa fa-bars fa-large"></i>}>
-          <Link
-            to="/"
-            className="text-decoration-none"
-            style={{ color: "inherit" }}
-          >
-            Player Menu
-          </Link>
+          <Logo />
         </CDBSidebarHeader>
         <CDBSidebarContent className="sidebar-content">
           <CDBSidebarMenu>

+ 21 - 0
src/components/Spoiler.js

@@ -0,0 +1,21 @@
+import { useState } from "react";
+import { Loader } from "./Loader";
+
+export const Spoiler = ({
+  open = false,
+  children = <Loader />,
+  header = "Spoiler",
+}) => {
+  const [visible, setVisible] = useState(open);
+  return (
+    <div className="Spoiler">
+      <div className="header" onClick={(e) => setVisible(!visible)}>
+        <div>
+          <span className="spoilerText">{header}</span>
+          <span className="spoilerArrow">{visible ? "▲" : "▼"}</span>
+        </div>
+      </div>
+      <div className="content">{visible && children}</div>
+    </div>
+  );
+};

BIN
src/images/default-avatar.jpg


+ 16 - 2
src/pages/Auth.js

@@ -3,6 +3,7 @@ import { NavDropdown } from "react-bootstrap";
 import { Link } from "react-router-dom";
 import { connect } from "react-redux";
 import { history } from "../App";
+import { backURL } from "../helpers";
 
 const Auth = ({ auth, promise, actionLogOut }) => {
   if (
@@ -20,8 +21,21 @@ const Auth = ({ auth, promise, actionLogOut }) => {
       {auth.payload ? (
         <NavDropdown
           id="dropdownProfileMenu"
-          // title={promise.user.payload.nick}
-          title="dick"
+          title={
+            <div className="pull-left d-inline-block">
+              <img
+                className="thumbnail-image avatarHeader"
+                src={
+                  promise?.user?.payload?.avatar
+                    ? `${backURL}${promise.user.payload.avatar.url}`
+                    : "https://i.ibb.co/bBxzmTm/default-avatar.jpg"
+                }
+                alt="Avatar"
+              />
+
+              {promise?.user?.payload?.nick}
+            </div>
+          }
           menuVariant="dark"
         >
           <NavDropdown.Item

+ 12 - 10
src/pages/Library.js

@@ -1,18 +1,17 @@
 import { connect } from "react-redux";
 import { AuthCheck } from "./../components/AuthCheck";
 import { history } from "./../App";
-import {
-  actionFindTracks,
-  actionFindUser,
-} from "./../actions/index";
+import { actionFindTracks, actionFindUser } from "./../actions/index";
 import { Button } from "react-bootstrap";
+import { CMyDropzone } from "../components/Dropzone";
 
-const Library = ({ auth, actionTracks, actionUser }) => {
+const Library = ({ auth, promise, actionTracks, actionUser }) => {
   return (
     <div className="SearchPage">
       {auth.token && history.location.pathname === "/library" ? (
         <div className="d-block mx-auto mt-2 container w-50">
-          <h1>Ваша библиотека с музыкой, {auth.payload.sub.nick}</h1>
+          <h1>Ваша библиотека с музыкой, {promise?.user?.payload?.nick}</h1>
+          <CMyDropzone />
           <Button onClick={() => actionTracks()}>Tracks</Button>
           <Button onClick={() => actionUser("61d45a16e9472933a6785f04")}>
             Me
@@ -27,7 +26,10 @@ const Library = ({ auth, actionTracks, actionUser }) => {
   );
 };
 
-export const CLibrary = connect((state) => ({ auth: state.auth }), {
-  actionTracks: actionFindTracks,
-  actionUser: actionFindUser,
-})(Library);
+export const CLibrary = connect(
+  (state) => ({ auth: state.auth, promise: state.promise }),
+  {
+    actionTracks: actionFindTracks,
+    actionUser: actionFindUser,
+  }
+)(Library);

+ 199 - 90
src/pages/Profile.js

@@ -3,109 +3,218 @@ import { history } from "./../App";
 import { Button, Form, Row, Col, Alert } from "react-bootstrap";
 import { AuthCheck } from "./../components/AuthCheck";
 import { useState } from "react";
+
 import {
   validateEmail,
   validatePassword,
   validateNickname,
 } from "./../helpers/index";
 import { actionUserUpdate } from "./../actions/index";
+import { Loader } from "../components/Loader";
+import { CMyDropzone } from "../components/Dropzone";
+import { Spoiler } from "../components/Spoiler";
 
 const Profile = ({ auth, promise, actionUpdate }) => {
-  const [login, setLogin] = useState(promise.user.payload.login);
+  // promise.user?.payload?.login
+  const [login, setLogin] = useState("");
+  // promise.user?.payload?.nick
+  const [nick, setNickname] = useState("");
   const [password, setPassword] = useState("");
-  const [nick, setNickname] = useState(promise.user.payload.nick);
-
-  return (
+  return promise?.user?.status === "PENDING" ? (
+    <Loader />
+  ) : (
     <div className="ProfilePage">
       {auth.token && history.location.pathname === "/profile" ? (
         <div className="d-block mx-auto mt-2 container w-50">
-          <h1>Ваш профиль, {promise.user.payload.nick}</h1>
-          <Form>
-            {validateEmail(login) ? null : (
-              <Alert>Email должен быть в формате: email@gmail.com.</Alert>
-            )}
-            {validatePassword(password) ? null : (
-              <Alert>
-                Пароль должен быть от 6 символов, иметь хотя бы одну цифру и
-                заглавную букву.
-              </Alert>
-            )}
-            {validateNickname(nick) ? null : (
-              <Alert>
-                Никнейм может состоять только из букв и цифр, а так же иметь
-                максимальную длину в 8 символов.
-              </Alert>
-            )}
-            <Form.Group
-              as={Row}
-              className="mb-3"
-              controlId="formHorizontalEmail"
-            >
-              <Form.Label column sm={2}>
-                Никнейм:
-              </Form.Label>
-              <Col sm={10}>
-                <Form.Control
-                  type="text"
-                  placeholder="Введите ваш новый никнейм"
-                  value={nick}
-                  max="8"
-                  onChange={(e) => setNickname(e.target.value)}
-                />
-              </Col>
-            </Form.Group>
-            <Form.Group
-              as={Row}
-              className="mb-3"
-              controlId="formHorizontalEmail"
-            >
-              <Form.Label column sm={2}>
-                Почта:
-              </Form.Label>
-              <Col sm={10}>
-                <Form.Control
-                  type="text"
-                  placeholder="Введите вашу новую почту"
-                  value={login}
-                  onChange={(e) => setLogin(e.target.value)}
-                />
-              </Col>
-            </Form.Group>
-            <Form.Group
-              as={Row}
-              className="mb-3"
-              controlId="formHorizontalPassword"
-            >
-              <Form.Label column sm={2}>
-                Пароль:
-              </Form.Label>
-              <Col sm={10}>
-                <Form.Control
-                  type="password"
-                  placeholder="Введите ваш новый пароль"
-                  onChange={(e) => setPassword(e.target.value)}
-                />
-              </Col>
-            </Form.Group>
-            <Form.Group as={Row} className="mb-3">
-              <Col sm={{ span: 10, offset: 2 }} className="my-3">
-                <Button
-                  variant="success"
-                  disabled={
-                    validateEmail(login) &&
-                    password.length !== 0 &&
-                    validatePassword(password) &&
-                    validateNickname(nick)
-                      ? false
-                      : true
-                  }
-                  onClick={() => actionUpdate(promise.user.payload._id)}
+          <h1>Ваш профиль, {promise?.user?.payload?.nick}</h1>
+          <Spoiler
+            children={
+              <>
+                <br />
+                <form
+                  action="/upload"
+                  method="post"
+                  enctype="multipart/form-data"
+                  id="form"
                 >
-                  Сохранить
-                </Button>
-              </Col>
-            </Form.Group>
-          </Form>
+                  <img
+                    className="avatarProfile"
+                    src={
+                      promise?.user?.payload?.avatar
+                        ? "https://dthezntil550i.cloudfront.net/kg/latest/kg1802132010216500004834729/1280_960/557d644f-12f3-49e1-bb66-23c16400540d.png"
+                        : "https://i.ibb.co/bBxzmTm/default-avatar.jpg"
+                    }
+                    alt="Avatar"
+                  />
+                  <CMyDropzone />
+                </form>
+              </>
+            }
+            header={<h3>Изменить аватар</h3>}
+          />
+          <Spoiler
+            children={
+              <>
+                <br />
+                <Form>
+                  {/*
+                {validateNickname(nick) ? null : (
+                  <Alert>
+                    Никнейм может состоять только из букв и цифр, а так же иметь
+                    максимальную длину в 8 символов.
+                  </Alert>
+                )} */}
+                  <Form.Group
+                    as={Row}
+                    className="m-2"
+                    controlId="formHorizontalEmail"
+                  >
+                    <Form.Label column sm={2}>
+                      Никнейм:
+                    </Form.Label>
+                    <Col sm={10}>
+                      <Form.Control
+                        type="text"
+                        placeholder="Введите ваш новый логин"
+                        value={nick}
+                        max="8"
+                        onChange={(e) => setNickname(e.target.value)}
+                      />
+                    </Col>
+                  </Form.Group>
+                  <Form.Group as={Row} className="mb-3">
+                    <Col sm={{ span: 10, offset: 2 }} className="my-3">
+                      <Button
+                        variant="success"
+                        disabled={
+                          validateEmail(login) &&
+                          password.length !== 0 &&
+                          validatePassword(password) &&
+                          validateNickname(nick)
+                            ? false
+                            : true
+                        }
+                        onClick={() =>
+                          actionUpdate(promise?.user?.payload?._id)
+                        }
+                      >
+                        Сохранить
+                      </Button>
+                    </Col>
+                  </Form.Group>
+                </Form>
+              </>
+            }
+            header={<h3>Изменить логин</h3>}
+          />
+          <Spoiler
+            children={
+              <>
+                <br />
+                <Form>
+                  {/* {validateEmail(login) ? null : (
+                  <Alert>Email должен быть в формате: email@gmail.com.</Alert>
+                )}
+                {validatePassword(password) ? null : (
+                  <Alert>
+                    Пароль должен быть от 6 символов, иметь хотя бы одну цифру и
+                    заглавную букву.
+                  </Alert>
+                )}
+                {validateNickname(nick) ? null : (
+                  <Alert>
+                    Никнейм может состоять только из букв и цифр, а так же иметь
+                    максимальную длину в 8 символов.
+                  </Alert>
+                )} */}
+                  <Form.Group
+                    as={Row}
+                    className="m-2"
+                    controlId="formHorizontalEmail"
+                  >
+                    <Form.Label column sm={2}>
+                      Почта:
+                    </Form.Label>
+                    <Col sm={10}>
+                      <Form.Control
+                        type="text"
+                        placeholder="Введите вашу новую почту"
+                        value={login}
+                        onChange={(e) => setLogin(e.target.value)}
+                      />
+                    </Col>
+                  </Form.Group>
+                  <Form.Group as={Row} className="mb-3">
+                    <Col sm={{ span: 10, offset: 2 }} className="my-3">
+                      <Button
+                        variant="success"
+                        disabled={
+                          validateEmail(login) &&
+                          password.length !== 0 &&
+                          validatePassword(password) &&
+                          validateNickname(nick)
+                            ? false
+                            : true
+                        }
+                        onClick={() =>
+                          actionUpdate(promise?.user?.payload?._id)
+                        }
+                      >
+                        Сохранить
+                      </Button>
+                    </Col>
+                  </Form.Group>
+                </Form>
+              </>
+            }
+            header={<h3>Изменить почту</h3>}
+          />
+          <Spoiler
+            children={
+              <>
+                <br />
+                <Form>
+                  <Form.Group
+                    as={Row}
+                    className="m-2"
+                    controlId="formHorizontalPassword"
+                  >
+                    <Form.Label column sm={2}>
+                      Пароль:
+                    </Form.Label>
+                    <Col sm={10}>
+                      <Form.Control
+                        type="password"
+                        placeholder="Введите ваш новый пароль"
+                        onChange={(e) => setPassword(e.target.value)}
+                      />
+                    </Col>
+                  </Form.Group>
+                  <Form.Group as={Row} className="mb-3">
+                    <Col sm={{ span: 10, offset: 2 }} className="my-3">
+                      <Button
+                        variant="success"
+                        disabled={
+                          validateEmail(login) &&
+                          password.length !== 0 &&
+                          validatePassword(password) &&
+                          validateNickname(nick)
+                            ? false
+                            : true
+                        }
+                        onClick={() =>
+                          actionUpdate(promise?.user?.payload?._id)
+                        }
+                      >
+                        Сохранить
+                      </Button>
+                    </Col>
+                  </Form.Group>
+                </Form>
+              </>
+            }
+            header={<h3>Изменить пароль</h3>}
+          />
         </div>
       ) : (
         <div className="d-block mx-auto mt-2 container w-50">

+ 3 - 73
src/reducers/index.js

@@ -1,61 +1,3 @@
-// import { jwtDecode } from "./../helpers/index";
-
-// export function promiseReducer(
-//   state = {},
-//   { type, status, payload, error, name }
-// ) {
-//   if (type === "PROMISE") {
-//     return {
-//       ...state,
-//       [name]: { status, payload, error },
-//     };
-//   }
-//   return state;
-// }
-
-// export function authReducer(state, { type, token, id, login, nick, avatar }) {
-//   if (!state) {
-//     if (localStorage.authToken) {
-//       type = "AUTH_LOGIN";
-//       token = localStorage.authToken;
-//     } else state = {};
-//   }
-//   if (type === "AUTH_LOGIN") {
-//     localStorage.setItem("authToken", token);
-//     let payload = jwtDecode(token);
-//     if (typeof payload === "object") {
-//       return {
-//         ...state,
-//         token,
-//         payload,
-//       };
-//     } else return state;
-//   }
-//   if (type === "ABOUT_ME") {
-//     let user = {
-//       id,
-//       login,
-//       nick,
-//       avatar,
-//     };
-//     if (id) {
-//       localStorage.setItem("user", JSON.stringify(user));
-//       return {
-//         ...state,
-//         id,
-//         login,
-//         nick,
-//         avatar,
-//       };
-//     } else return state;
-//   }
-//   if (type === "AUTH_LOGOUT") {
-//     localStorage.removeItem("authToken");
-//     localStorage.removeItem("user");
-//     return {};
-//   }
-// }
-
 import { jwtDecode } from "./../helpers/index";
 
 export function promiseReducer(
@@ -71,7 +13,7 @@ export function promiseReducer(
   return state;
 }
 
-export function authReducer(state, { type, token, id, login, nick, avatar }) {
+export function authReducer(state, { type, token }) {
   if (!state) {
     if (localStorage.authToken) {
       type = "AUTH_LOGIN";
@@ -79,21 +21,9 @@ export function authReducer(state, { type, token, id, login, nick, avatar }) {
     } else state = {};
   }
   if (type === "AUTH_LOGIN") {
-    localStorage.setItem("authToken", token);
-    let payload = jwtDecode(token);
-    if (typeof payload === "object") {
-      return {
-        ...state,
-        token,
-        payload,
-      };
-    } else return state;
-  }
-  if (type === "ABOUT_ME") {
-    localStorage.setItem("user", JSON.stringify("object"));
-    localStorage.setItem("authToken", token);
     let payload = jwtDecode(token);
-    if (typeof payload === "object") {
+    if (!!token && typeof payload === "object") {
+      localStorage.authToken = token;
       return {
         ...state,
         token,