Prechádzať zdrojové kódy

add routes, userBar, userProfile, change structure

Vitalii Polishchuk 2 rokov pred
rodič
commit
9fe63ef9fa

+ 1 - 3
src/App.css

@@ -1,7 +1,5 @@
-.App {
+.app {
   height: 100vh;
-  display: flex;
-  justify-content: center;
 }
 
 .reg-form {

+ 13 - 76
src/App.js

@@ -1,92 +1,29 @@
 import './App.css';
-import { BrowserRouter as Router, Route, Link, Switch, Redirect } from 'react-router-dom';
-import createHistory from "history/createBrowserHistory";
-import React, { useState } from 'react';
-import { Provider, connect } from 'react-redux';
+import React from 'react';
+import { Provider } from 'react-redux';
 import store from './reducers';
-import { actionFullLogin, actionFullRegister, actionUploadFile, actionGetFile, actionFullAddChat, actionFullEditMSG, actionFullGetChats, actionAddMSG } from './actions';
-import { MyDropzone } from './components/dropzone';
-import { LoginForm } from './components/loginForm';
-import { RegistrationForm } from './components/registrationForm';
-import { ConnectChatOnline, ConnectMain } from './pages/main';
+import ConnectRoutes from './components/routes';
 
-//for tests
-// let msgARR = [{ "_id": "615e0b5fdd6e923e41f3348e", "text": "hi111", "createdAt": "1633553247000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } },
-// { "_id": "615e0c88dd6e923e41f3348f", "text": "hi111", "createdAt": "1633553544000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } },
-// { "_id": "615e0e02dd6e923e41f33490", "text": "hi111", "createdAt": "1633553922000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } },
-// { "_id": "615e102cdd6e923e41f33492", "text": "hi", "createdAt": "1633554476000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } },
-// { "_id": "615e113bdd6e923e41f33493", "text": "hi111", "createdAt": "1633554747000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } },
-// { "_id": "615e12cddd6e923e41f33495", "text": "hi111", "createdAt": "1633555149000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } },
-// { "_id": "615e146bdd6e923e41f33497", "text": "hi111", "createdAt": "1633555563000", "owner": { "login": "test1111", "_id": "61460c90ae7d905e54d32d11", "nick": null } }]
 
-// let chatsASS = {
-//   1: {
-//     title: "первый",
-//     lastModified: "токошо",
-//     avatar: {
-//       url: "/опа"
-//     },
-//     messages: [{}, {}, { text: "привет" }]
-//   },
-//   2: {
-//     title: "второй"
-//   },
-//   3: {
-//     title: "третий"
-//   }
-// }
-
-const Preloading = ({ promiseName, promiseState, children }) => {
-  return (
-    <>
-      {(promiseState[promiseName] && promiseState[promiseName].status === "RESOLVED") ? children : null}
-    </>
-  )
-}
-
-const CPreloading = connect(state => ({ promiseState: state.promise }))
-// const PrivateRoute = ({ component, roles, auth, fallback = "/login", ...originalProps }) => {
-//   const PageWrapper = (pageProps) => {
-//     const OriginalPage = component
-//     //сопоставить роли с аус
-//     if (пересечение роли с аус(ацл из него) в наличии) {
-//       return <OriginalPage {...pageProps} />
-//     } else {
-//       return <Redirect to={fallback} />
-//     }
-//   }
+// const Preloading = ({ promiseName, promiseState, children }) => {
 //   return (
-//     <Route component={PageWrapper} {...originalProps} />
+//     <>
+//       {(promiseState[promiseName] && promiseState[promiseName].status === "RESOLVED") ? children : null}
+//     </>
 //   )
 // }
-// const MyUltraRoute = connect(сделать аус)(PrivateRoute)
 
-const ConnectLoginForm = connect(null, { onLogin: actionFullLogin })(LoginForm)
-
-const ConnectRegistrationForm = connect(null, { onRegistration: actionFullRegister })(RegistrationForm)
-
-const ConnectDropZone = connect(null, { onUpload: actionUploadFile })(MyDropzone)
+// const CPreloading = connect(state => ({ promiseState: state.promise }))
 
 store.subscribe(() => console.log(store.getState()))
 
-// console.log(store.dispatch(actionGetFile("61534453dd6e923e41f3344c")))
-// store.dispatch(actionFullGetChats("61460c90ae7d905e54d32d11"))
-
 function App() {
   return (
-    <Provider store={store}>
-      <Router history={createHistory()}>
-        <div className="App">
-          <Switch>
-
-            <Route path="/registration" component={ConnectRegistrationForm} exact />
-            <Route path="/login" component={ConnectLoginForm} exact />
-            <Route path="/:_id" component={ConnectMain} />
-            <Redirect from="/" to="/chat" />
-          </Switch>
-        </div >
-      </Router>
-    </Provider>
+    <div className="app">
+      <Provider store={store}>
+        <ConnectRoutes />
+      </Provider>
+    </div >
   );
 }
 

+ 113 - 63
src/actions/index.js

@@ -1,5 +1,3 @@
-//потом почищу лишнее
-
 const getGQL = url => {
   return (query, variables) => {
     return fetch(url, {
@@ -71,42 +69,6 @@ let userChats = async (id) => {
   return result
 }
 
-let chatMSG = async (id) => {
-  let query = `query getMSG($chatID: String){
-        MessageFind(query: $chatID){
-         _id text createdAt media{
-           url _id type
-         } owner{
-          login _id nick
-          } replyTo {
-            _id text createdAt owner{
-              login _id nick
-              }
-          }
-        }
-      }`
-
-  let qVariables = {
-    "chatID": JSON.stringify([{ "chat._id": id }])
-  }
-
-  let result = await gql(query, qVariables)
-  return result
-}
-
-let msgCount = async (chat_id) => {
-  let query = `query msgCount($query: String){
-        MessageCount(query: $query)
-      }`
-
-  let qVariables = {
-    "query": JSON.stringify([{ "chat._id": chat_id }])
-  }
-
-  let result = await gql(query, qVariables)
-  return result
-}
-
 let chatSortMSG = async (id, skipCount) => {
   let query = `query getMSG($query: String){
     MessageFind(query: $query){
@@ -126,8 +88,6 @@ let chatSortMSG = async (id, skipCount) => {
     "query": JSON.stringify([{ "chat._id": id }, { sort: [{ _id: -1 }], skip: [skipCount], limit: [30] }])
   }
 
-  //меняем skip по событию скролла вверх
-
   let result = await gql(query, qVariables)
   return result
 }
@@ -285,15 +245,17 @@ let editMSG = async (id, text) => {
   return result
 }
 
-let fileFound = async (id) => {
-  let query = `query findMedia($id: String){
-        MediaFind(query: $id){
-          url type originalFileName
-        }
-      }`
+let userInfo = async (id) => {
+  let query = `query user($uid: String){
+    UserFindOne(query: $uid){
+      _id login nick avatar{
+        _id url
+      }
+    }
+  }`
 
   let qVariables = {
-    "id": JSON.stringify([{ "_id": id }])
+    "uid": JSON.stringify([{ "_id": id }])
   }
 
   let result = await gql(query, qVariables)
@@ -321,7 +283,62 @@ let userSearch = async (searchInput) => {
   }
 
   let result = await gql(query, qVariables)
-  console.log(result)
+  return result
+}
+
+let changeNick = async (userID, nick) => {
+  let query = `mutation passwordChange($input: UserInput){
+    UserUpsert(user: $input){
+      _id
+    }
+  }`
+
+  let qVariables = {
+    "input": {
+      "_id": userID,
+      "nick": nick
+    }
+  }
+
+  let result = await gql(query, qVariables)
+  return result
+}
+
+let changeUserAvatar = async (userID, imageID) => {
+  let query = `mutation setUserAvatar($avatar: MediaInput){
+    MediaUpsert(media: $avatar){
+      _id text url
+    }
+  }`
+
+  let qVariables = {
+    "avatar": {
+      "_id": imageID,
+      "userAvatar": {
+        "_id": userID
+      }
+    }
+  }
+
+  let result = await gql(query, qVariables)
+  return result
+}
+
+let changeUserPassword = async (userID, newPassword) => {
+  let query = `mutation passwordChange($input: UserInput){
+    UserUpsert(user: $input){
+      _id
+    }
+  }`
+
+  let qVariables = {
+    "input": {
+      "_id": userID,
+      "password": newPassword
+    }
+  }
+
+  let result = await gql(query, qVariables)
   return result
 }
 
@@ -342,9 +359,9 @@ const actionPromise = (name, promise) =>
     }
   }
 
-export const actionAuthLogin = token => ({ type: "LOGIN", token })
+const actionAuthLogin = token => ({ type: "LOGIN", token })
 
-export const actionLogin = (login, password) => actionPromise("login", log(login, password))
+const actionLogin = (login, password) => actionPromise("login", log(login, password))
 
 export const actionFullLogin = (login, password) => {
   return async (dispatch) => {
@@ -383,11 +400,47 @@ let upload = async (files) => {
   }).then(res => res.json())
 }
 
+export const actionChangeUserAvatar = (userID, imageID) => actionPromise("changeUserAvatar", changeUserAvatar(userID, imageID))
+
+export const actionChangeUserPassword = (userID, newPassword) => actionPromise("changeUserPassword", changeUserPassword(userID, newPassword))
+
+export const actionChangeUserNick = (userID, nick) => actionPromise("changeUserNick", changeNick(userID, nick))
+
+export const actionFullChangeUserNick = (userID, nick) => {
+  return async (dispatch) => {
+    let result = await dispatch(actionChangeUserNick(userID, nick))
+
+    if (result.data.UserUpsert !== null) {
+      dispatch(actionGetUserInfo(userID))
+    }
+  }
+}
+
+export const actionFullChangeUserAvatar = (userID, imageID) => {
+  return async (dispatch) => {
+    let result = await dispatch(actionChangeUserAvatar(userID, imageID))
+
+    if (result.data.UserUpsert !== null) {
+      dispatch(actionGetUserInfo(userID))
+    }
+  }
+}
+
+export const actionFullChangeUserPassword = (userID, newPassword) => {
+  return async (dispatch) => {
+    let result = await dispatch(actionChangeUserPassword(userID, newPassword))
+
+    if (result.data.UserUpsert !== null) {
+      dispatch(actionGetUserInfo(userID))
+    }
+  }
+}
+
 export const actionUploadFile = (files) => actionPromise("upload", upload(files))
 
 export const actionAuthLogout = () => ({ type: "LOGOUT" })
 
-export const actionGetFile = (id) => actionPromise("fileFound", fileFound(id))
+export const actionGetUserInfo = (id) => actionPromise("userInfo", userInfo(id))
 
 const actionGetChats = (id) => actionPromise("chats", userChats(id))
 
@@ -451,19 +504,17 @@ export const actionFullAddChat = (title, members) => {
 export const actionFullEditChat = (chat_id, title, avatar, members) => {
   return async (dispatch) => {
     let result = await dispatch(actionEditChatBack(chat_id, title, members))
-    let avatarResult = await dispatch(actionSetAvatar(avatar, chat_id))
-    console.log(avatarResult)
+    let avatarResult
 
-    if (result.data.ChatUpsert && avatarResult.data.MediaUpsert) {
-      dispatch(actionEditChat(chat_id, title, avatarResult.data.MediaUpsert, members))
+    if (avatar) {
+      avatarResult = await dispatch(actionSetAvatar(avatar, chat_id))
     }
-  }
-}
-
-export const actionFullAddMessage = (chat_id, text, media) => {
-  return async (dispatch) => {
-    let result = await dispatch(actionAddMSGBack(chat_id, text, media))
 
+    if (result.data.ChatUpsert && avatarResult?.data.MediaUpsert) {
+      dispatch(actionEditChat(chat_id, title, avatarResult.data.MediaUpsert, members))
+    } else if (result.data.ChatUpsert) {
+      dispatch(actionEditChat(chat_id, title, null, members))
+    }
   }
 }
 
@@ -476,5 +527,4 @@ export const actionFullEditMSG = (message_id, text, media) => {
       result.data.MessageUpsert.text,
       result.data.MessageUpsert.media))
   }
-}
-
+}

+ 16 - 7
src/components/chatEditForm.js

@@ -1,19 +1,20 @@
 import { useEffect, useState } from "react"
 import { connect } from "react-redux"
-import { actionEditChat, actionFullEditChat, actionUploadFile, actionUserSearch } from "../actions"
+import { actionFullEditChat, actionUserSearch } from "../actions"
 import { MemberList, UserSearch } from "./chatForm"
-import { MyDropzone } from "./dropzone"
+import { ConnectDropzone } from "./dropzone"
 
-export const ChatEditForm = ({ chat_id, chat, searchState, onUpload, onUserSearch, onChangeChat }) => {
+const ChatEditForm = ({ chat_id, chat, searchState, onUserSearch, onChangeChat }) => {
     let [edit, setEdit] = useState(false)
     let [newTitle, setNewTitle] = useState(chat.title || "")
     let [newAvatar, setNewAvatar] = useState([])
     let [newMembers, setNewMembers] = useState(chat.members)
 
+    console.log(newAvatar)
     useEffect(() => {
         setEdit(false)
         setNewTitle(chat.title)
-        setNewAvatar(chat.avatar)
+        setNewAvatar([chat.avatar])
         setNewMembers(chat.members)
     }, [chat])
 
@@ -35,16 +36,24 @@ export const ChatEditForm = ({ chat_id, chat, searchState, onUpload, onUserSearc
             {edit ? <input onChange={(e) => setNewTitle(e.target.value)} value={newTitle} /> : <span>Название чата: {chat.title}</span>}
             {edit ? <div>
                 <span>Загрузите новый аватар</span>
-                <MyDropzone maxFiles={1} onUpload={onUpload} onSet={setNewAvatar} />
+                <ConnectDropzone maxFiles={1} onSet={setNewAvatar} />
             </div> :
-                chat.avatar && <img src={"/" + chat.avatar.url} />}
+                chat.avatar && <img src={"/" + chat.avatar.url} alt="chatAvatar" />}
 
             {<MemberList members={newMembers} onDeleteMember={edit && memberDeleteHandler} />}
             {edit && <UserSearch searchState={searchState} onUserSearch={onUserSearch} onAddMember={userAddHandler} />}
             <div className="edit-btn-container">
                 <button onClick={() => editChatHandler()} >{edit ? "Отменить редактирование" : "Редактировать чат"}</button>
-                {edit && <button onClick={() => onChangeChat(chat_id, newTitle, newAvatar && newAvatar[0]._id, newMembers)}>Применить изменения</button>}
+                {edit && <button onClick={() => onChangeChat(chat_id, newTitle, newAvatar[0]?._id, newMembers)}>Применить изменения</button>}
             </div>
         </div>
     )
 }
+
+const ConnectChatEditForm = connect(state => ({ searchState: state.promise?.userSearch?.payload?.data?.UserFind }),
+    {
+        onUserSearch: actionUserSearch,
+        onChangeChat: actionFullEditChat
+    })(ChatEditForm)
+
+export default ConnectChatEditForm

+ 1 - 1
src/components/chatForm.js

@@ -19,7 +19,7 @@ export const UserSearch = ({ searchState, onUserSearch, onAddMember }) => {
 
     useEffect(() => {
         searchInput && setUsers(searchState)
-    }, [searchState])
+    }, [searchState, searchInput])
 
     let searchHandler = async (e) => {
         setSearchInput(e.target.value)

+ 3 - 2
src/components/chatWindow.js

@@ -6,7 +6,7 @@ const updateScroll = (el) => {
     el.scrollTop = el.scrollHeight
 }
 
-const Chat = ({ chat_id, chat_title, user_id, messages, onUpload, onSend, onMSGEdit, onScrollTopComplete }) => {
+const Chat = ({ chat_id, chat_title, user_id, messages, onUpload, onSend, onMSGEdit, onScrollTopComplete, onInfo }) => {
     let [msg, setMSG] = useState("")
     let [isUpload, setIsUpload] = useState(false)
     let [files, setFiles] = useState([])
@@ -22,7 +22,7 @@ const Chat = ({ chat_id, chat_title, user_id, messages, onUpload, onSend, onMSGE
         if (isScroll) {
             updateScroll(scrollRef.current)
         }
-    }, [messages])
+    }, [messages, isScroll])
 
     useEffect(() => {
         input.current && input.current.focus()
@@ -80,6 +80,7 @@ const Chat = ({ chat_id, chat_title, user_id, messages, onUpload, onSend, onMSGE
         <div className="chat-window">
             <div className="chat-nav">
                 <span>{chat_title}</span>
+                <button onClick={() => onInfo()}>Инфо</button>
             </div>
 
             <ul ref={scrollRef} onScroll={() => scrollHandler(scrollRef.current)} className="message-list">

+ 5 - 2
src/components/chatsList.js

@@ -3,6 +3,7 @@ import ReactTimeAgo from 'react-time-ago'
 import TimeAgo from 'javascript-time-ago'
 import ru from 'javascript-time-ago/locale/ru.json'
 import React from 'react';
+import { connect } from "react-redux";
 
 TimeAgo.addLocale(ru)
 
@@ -23,7 +24,7 @@ const ChatItem = ({ id, chat }) => {
     )
 }
 
-const ChatsList = ({ chats, onChat }) => {
+const ChatsList = ({ chats }) => {
     return (
         <>
             <ul className="chat-list">
@@ -36,4 +37,6 @@ const ChatsList = ({ chats, onChat }) => {
     )
 }
 
-export default ChatsList
+const ConnectChatsList = connect(state => ({ chats: state.chat }))(ChatsList)
+
+export default ConnectChatsList

+ 1 - 1
src/components/dropzone.js

@@ -12,7 +12,7 @@ export const MyDropzone = ({ maxFiles, onUpload, onSet }) => {
             console.log(files)
         }))
         onSet(files)
-    }, [onUpload])
+    }, [onUpload, onSet])
 
     const { acceptedFiles, getRootProps, getInputProps } = useDropzone({ onDrop, maxFiles: maxFiles })
 

+ 2 - 0
src/components/history.js

@@ -0,0 +1,2 @@
+import { createBrowserHistory } from "history";
+export default createBrowserHistory();

+ 5 - 1
src/components/loginForm.js

@@ -1,6 +1,8 @@
 import { useState } from 'react';
+import { connect } from 'react-redux';
+import { actionFullLogin } from '../actions';
 
-export const LoginForm = ({ onLogin }) => {
+const LoginForm = ({ onLogin }) => {
     const [login, setLogin] = useState("")
     const [password, setPassword] = useState("")
 
@@ -12,3 +14,5 @@ export const LoginForm = ({ onLogin }) => {
         </div>
     )
 }
+
+export const ConnectLoginForm = connect(null, { onLogin: actionFullLogin })(LoginForm)

+ 4 - 4
src/components/message.js

@@ -93,25 +93,25 @@ export const Message = ({ id, nick, msg, date, media, own = false, replyTo, onMS
                                 {file.type && file.type.includes("audio") &&
                                     <audio className="msg-media-audio" preload="metadata" controls>
                                         <source src={"/" + file.url} type={file.type} />
-                                        <a href={"/" + file.url} />
+                                        <a href={"/" + file.url}>Скачать</a>
                                     </audio>
                                 }
 
                                 {
                                     file.type && file.type.includes("image") &&
-                                    <img className="msg-media-img" src={"/" + file.url} alt="image" />
+                                    <img className="msg-media-img" src={"/" + file.url} alt="media" />
                                 }
 
                                 {
                                     file.type && file.type.includes("video") &&
                                     <video className="msg-media-video" preload="metadata" controls>
                                         <source src={"/" + file.url} type={file.type} />
-                                        <a href={"/" + file.url} />
+                                        <a href={"/" + file.url}>Скачать</a>
                                     </video>
                                 }
 
                                 {
-                                    file.type === null && <a href={"/" + file.url} />
+                                    file.type === null && <a href={"/" + file.url}>Скачать</a>
                                 }
 
                             </li>)

+ 76 - 0
src/components/profile.js

@@ -0,0 +1,76 @@
+import { useState } from "react"
+import { connect } from "react-redux"
+import { actionFullChangeUserAvatar, actionFullChangeUserNick, actionFullChangeUserPassword } from "../actions"
+import { ConnectDropzone } from "./dropzone"
+
+const ProfileEdit = ({ userID, onChangeAvatar, onChangeNick, onChangePassword }) => {
+    let [avatar, setAvatar] = useState(false)
+    let [avatarData, setAvatarData] = useState([])
+    let [nick, setNick] = useState(false)
+    let [nickData, setNickData] = useState("")
+    let [password, setPassword] = useState(false)
+    let [passwordData, setPasswordData] = useState("")
+    let [passwordValidation, setPasswordValidation] = useState("")
+    const validation = new RegExp(`^(?=.*[0-9]).{${4},}$`)
+
+    let applyHandler = (type) => {
+        if (type === "avatar") {
+            onChangeAvatar(userID, avatarData[0]._id)
+            setAvatar(!avatar)
+            setAvatarData([])
+        }
+
+        if (type === "nick") {
+            onChangeNick(userID, nickData)
+            setNick(!nick)
+        }
+
+        if (type === "password") {
+            onChangePassword(userID, passwordData)
+            setPassword(!password)
+            setPasswordData("")
+        }
+    }
+
+    return (
+        <div>
+            <div>
+                <button onClick={() => setAvatar(!avatar)}>Аватар</button>
+                {avatar &&
+                    <>
+                        <ConnectDropzone maxFiles={1} onSet={setAvatarData} />
+                        <button onClick={() => applyHandler("avatar")}>Установить</button>
+                    </>}
+            </div>
+            <div>
+                <button onClick={() => setNick(!nick)}>Ник</button>
+                {nick &&
+                    <>
+                        <input onChange={(e) => setNickData(e.target.value)} value={nickData} placeholder="введите новый ник" />
+                        <button onClick={() => applyHandler("nick")}>Установить</button>
+                    </>}
+            </div>
+            <div>
+                <button onClick={() => setPassword(!password)}>Пароль</button>
+                {password &&
+                    <>
+
+                        <input type='password' value={passwordData}
+                            onChange={(e) => setPasswordData(e.target.value)} placeholder="введите новый пароль" />
+                        <input type='password' value={passwordValidation}
+                            onChange={e => setPasswordValidation(e.target.value)} placeholder="повторите новый пароль" />
+                        <button disabled={!(passwordData === passwordValidation && validation.test(passwordData))}
+                            onClick={() => applyHandler("password")}>Установить</button>
+                    </>}
+            </div>
+            <a href="/chat">Вернуться на главную</a>
+        </div>
+    )
+}
+
+export const ConnectProfileEdit = connect(state => ({ userID: state.auth.payload.sub.id }),
+    {
+        onChangeAvatar: actionFullChangeUserAvatar,
+        onChangeNick: actionFullChangeUserNick,
+        onChangePassword: actionFullChangeUserPassword
+    })(ProfileEdit)

+ 24 - 0
src/components/profileBar.js

@@ -0,0 +1,24 @@
+import { connect } from "react-redux"
+import { actionAuthLogout } from "../actions"
+
+const Profile = ({ user, onLogout }) => {
+    return (
+        <>
+            {user && <div>
+                {user.avatar ?
+                    <img src={user.avatar.url} alt="ava" />
+                    :
+                    <img src="https://img.icons8.com/external-bearicons-glyph-bearicons/64/000000/external-User-essential-collection-bearicons-glyph-bearicons.png"
+                        alt="ava" />}
+                <span>{user.nick || user.login}</span>
+                <a href="/profile">Редактировать профиль</a>
+                <button onClick={() => onLogout()}>Выйти</button>
+            </div>}
+        </>
+    )
+}
+
+export const ConnectProfile = connect(state => ({ user: state.promise.userInfo?.payload?.data?.UserFindOne }),
+    {
+        onLogout: actionAuthLogout
+    })(Profile)

+ 6 - 2
src/components/registrationForm.js

@@ -1,6 +1,8 @@
 import { useState } from 'react';
+import { connect } from 'react-redux'
+import { actionFullRegister } from '../actions'
 
-export const RegistrationForm = ({ onRegistration }) => {
+const RegistrationForm = ({ onRegistration }) => {
     const [login, setLogin] = useState("")
     const [password, setPassword] = useState("")
     const [passwordValidation, setPasswordValidation] = useState("")
@@ -15,4 +17,6 @@ export const RegistrationForm = ({ onRegistration }) => {
             <button disabled={!(password === passwordValidation && validation.test(password))} onClick={() => onRegistration(login, password)}>Registration</button>
         </div>
     )
-}
+}
+
+export const ConnectRegistrationForm = connect(null, { onRegistration: actionFullRegister })(RegistrationForm)

+ 47 - 0
src/components/routes.js

@@ -0,0 +1,47 @@
+import { connect } from 'react-redux';
+import { Router, Route, Redirect, Switch } from 'react-router-dom';
+import history from '../components/history';
+import { ConnectMain } from '../pages/main';
+import { ConnectLoginForm } from '../components/loginForm';
+import { ConnectRegistrationForm } from '../components/registrationForm';
+import { ConnectProfileEdit } from '../components/profile';
+
+const PrivateRoute = ({ component, isAuth, log, fallback, ...originalProps }) => {
+    const PageWrapper = (pageProps) => {
+        const OriginalPage = component
+        if (log && !isAuth) {
+            return <OriginalPage {...pageProps} />
+        } else if (!isAuth) {
+            return <Redirect to={fallback} />
+        } else if (log && isAuth) {
+            return <Redirect to={fallback} />
+        } else {
+            return <OriginalPage {...pageProps} />
+        }
+    }
+
+    return (
+        <Route component={PageWrapper} {...originalProps} />
+    )
+}
+
+const ConnectPrivateRoute = connect(state => ({ isAuth: state?.auth?.token }))(PrivateRoute)
+
+const ChatRoutes = () => {
+
+    return (
+        <Router history={history}>
+            <Switch>
+                <ConnectPrivateRoute path="/login" component={ConnectLoginForm} log={true} fallback="/chat" exact />
+                <ConnectPrivateRoute path="/registration" component={ConnectRegistrationForm} log={true} fallback="/chat" exact />
+                <ConnectPrivateRoute path="/profile" component={ConnectProfileEdit} fallback="/login" />
+                <ConnectPrivateRoute path="/:_id" component={ConnectMain} fallback="/login" />
+                <Redirect from="/" to="/chat" />
+            </Switch>
+        </Router>
+    )
+}
+const ConnectRoutes = connect(state => ({ isAuth: state.auth.token }))(ChatRoutes)
+
+
+export default ConnectRoutes

+ 0 - 0
src/pages/404.js


+ 0 - 0
src/pages/login.js


+ 40 - 41
src/pages/main.js

@@ -1,19 +1,28 @@
-import Chat from "../components/chatWindow";
-import ChatsList from "../components/chatsList";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
 import io from 'socket.io-client';
 import { connect } from "react-redux";
-import { actionAddChat, actionAddChatBack, actionAddMSG, actionAddMSGBack, actionEditChat, actionEditMSG, actionEditMSGback, actionFullEditChat, actionFullGetChats, actionFullGetMessages, actionUploadFile, actionUserSearch } from "../actions";
+import Chat from "../components/chatWindow";
+import ChatsList from "../components/chatsList";
 import ConnectChatForm from "../components/chatForm";
-import { ChatEditForm } from "../components/chatEditForm";
+import ConnectChatEditForm from "../components/chatEditForm";
+import { ConnectProfile } from "../components/profileBar";
+import {
+    actionAddChat, actionAddChatBack, actionAddMSG, actionAddMSGBack,
+    actionEditMSGback, actionFullGetChats, actionFullGetMessages,
+    actionGetUserInfo, actionUploadFile
+} from "../actions";
+
+const Main = ({ match: { params: { _id } }, userID, chats, getUserInfo, getChat, getMessages, addChat, addMSG, editMSG, sendMSG, addFile }) => {
+    let [isEdit, setIsEdit] = useState(false)
+    let onInfoHandler = () => {
+        setIsEdit(!isEdit)
+    }
 
-//все в куче
-const Main = ({ match: { params: { _id } }, userID, chats, getChat, getMessages, addChat, editChat, addMSG, editMSG, editMSGState, sendMSG, search, userSearch, changeChat, addFile }) => {
     let chat_id = _id.split(".")[1]
 
     useEffect(() => {
+        getUserInfo(userID)
         getChat(userID)
-        console.log("монтируем")
 
         const socket = io("ws://chat.fs.a-level.com.ua")
 
@@ -35,65 +44,55 @@ const Main = ({ match: { params: { _id } }, userID, chats, getChat, getMessages,
         socket.on("disconnect", () => console.log("дисконект", socket.id));
 
         socket.on('chat', chat => {
-            console.log("это пришло по сокету (чат)", chat)
             addChat(chat._id, chat.title, chat.createdAt, chat.lastModified, chat.owner, chat.avatar, chat.messages, chat.members)
         })
 
         socket.on('msg', msg => {
-            console.log("это пришло по сокету (сообщение)", msg)
             addMSG(true, msg.chat._id, msg._id, msg.text, msg.createdAt, msg.owner, msg.media, msg.replyTo)
         })
-
-        return () => {
-            console.log("размонтировали")
-        }
     }, [])
 
     useEffect(() => {
         chat_id && getMessages(chat_id, 0)
-    }, [_id])
+    }, [_id, chat_id, getMessages])
 
     return (
-        <main>
-            <ChatsList chats={chats} />
-            {_id === "chat" && <ConnectChatForm />}
-
-            {chats[chat_id] &&
-                <Chat chat_id={chat_id}
-                    chat_title={chats[chat_id].title}
-                    chat_avatar={chats[chat_id].avatar?.url}
-                    user_id={userID}
-                    messages={chats[chat_id].messages || []}
-                    onUpload={addFile}
-                    onSend={sendMSG}
-                    onMSGEdit={editMSG}
-                    onScrollTopComplete={getMessages} />}
-
-            {chats[chat_id] && <ChatEditForm chat_id={chat_id}
-                chat={chats[chat_id]}
-                searchState={search}
-                onUserSearch={userSearch}
-                onChangeChat={changeChat}
-                onUpload={addFile} />}
-        </main>
+        <>
+            <ConnectProfile />
+            <main>
+                <ChatsList />
+                {_id === "chat" && <ConnectChatForm />}
+
+                {chats[chat_id] &&
+                    <Chat chat_id={chat_id}
+                        chat_title={chats[chat_id].title}
+                        chat_avatar={chats[chat_id].avatar?.url}
+                        user_id={userID}
+                        messages={chats[chat_id].messages || []}
+                        onUpload={addFile}
+                        onSend={sendMSG}
+                        onMSGEdit={editMSG}
+                        onScrollTopComplete={getMessages}
+                        onInfo={onInfoHandler} />}
+
+                {isEdit && chats[chat_id] && <ConnectChatEditForm chat_id={chat_id} chat={chats[chat_id]} />}
+            </main>
+        </>
     )
 }
 
 export const ConnectMain = connect(state => ({
     userID: state.auth.payload.sub.id,
-    search: state.promise?.userSearch?.payload?.data?.UserFind,
     chats: state.chat,
 }),
     {
+        getUserInfo: actionGetUserInfo,
         getChat: actionFullGetChats,
         getMessages: actionFullGetMessages,
         addChat: actionAddChat,
         addMSG: actionAddMSG,
         editMSG: actionEditMSGback,
-        editMSGState: actionEditMSG,
         sendMSG: actionAddMSGBack,
-        userSearch: actionUserSearch,
         newChat: actionAddChatBack,
-        changeChat: actionFullEditChat,
         addFile: actionUploadFile
     })(Main)

+ 0 - 0
src/pages/registration.js


+ 10 - 3
src/reducers/chat.js

@@ -1,4 +1,7 @@
-function chatReducer(state = {}, { type, chat_id, title, createdAt, lastModified, avatar, members, messages, msg_id, msg_text, msg_createdAt, msg_owner, msg_media, msg_replyTo, socket = false }) {
+function chatReducer(state = {},
+    { type, chat_id, title, createdAt, lastModified, avatar, members,
+        messages, msg_id, msg_text, msg_createdAt, msg_owner, msg_media, msg_replyTo, socket = false }) {
+
     if (type === 'CHAT') {
         if (Object.keys(state).filter(id => id === chat_id).length === 0) { //новый чат
             return {
@@ -22,7 +25,7 @@ function chatReducer(state = {}, { type, chat_id, title, createdAt, lastModified
                 [chat_id]: {
                     ...state[chat_id],
                     title: title,
-                    avatar: avatar,
+                    avatar: avatar || state[chat_id].avatar,
                     members: members
                 }
             }
@@ -39,7 +42,11 @@ function chatReducer(state = {}, { type, chat_id, title, createdAt, lastModified
                     createdAt: state[chat_id].createdAt,
                     lastModified: state[chat_id].lastModified > msg_createdAt ? state[chat_id].lastModified : msg_createdAt,
                     avatar: state[chat_id].avatar,
-                    messages: socket ? [...state[chat_id].messages, { _id: msg_id, text: msg_text, createdAt: msg_createdAt, owner: msg_owner, media: msg_media, replyTo: msg_replyTo }] : [{ _id: msg_id, text: msg_text, createdAt: msg_createdAt, owner: msg_owner, media: msg_media, replyTo: msg_replyTo }, ...state[chat_id].messages],
+                    messages: socket ?
+                        [...state[chat_id].messages,
+                        { _id: msg_id, text: msg_text, createdAt: msg_createdAt, owner: msg_owner, media: msg_media, replyTo: msg_replyTo }]
+                        : [{ _id: msg_id, text: msg_text, createdAt: msg_createdAt, owner: msg_owner, media: msg_media, replyTo: msg_replyTo },
+                        ...state[chat_id].messages],
                     members: state[chat_id].members
                 }
             }