Przeglądaj źródła

the architecture of the project has been redesigned. The reducer has been split into PostOne and PostTape. fragmented components with nested logic. added media upload limit up to 8. fixed display of likes of sub comments. Added reverse scroll when displaying comments on the post page. The action is divided into actionGetGql and actionCreators. Function refactoring in redux-saga - add/remove likes and comments, subscribe and unsubscribe. fixed error when validating fields in comments, added input character limit

makstravm 2 lat temu
rodzic
commit
a3e04cfc7e
53 zmienionych plików z 1521 dodań i 1219 usunięć
  1. 29 16
      src/App.js
  2. 7 2
      src/App.scss
  3. 87 133
      src/actions/index.js
  4. 138 0
      src/actions/actonsCreators.js
  5. 6 6
      src/components/main/profilePage/EditAvatar.js
  6. 24 25
      src/components/main/postsFeed/FieldComment.jsx
  7. 0 11
      src/pages/Content.jsx
  8. 0 11
      src/components/Footer.jsx
  9. 9 0
      src/components/FooterComponent.jsx
  10. 6 6
      src/components/FormAuthorization.jsx
  11. 55 0
      src/components/GalleryMediaPostsUser.jsx
  12. 75 0
      src/components/ModalFollow.jsx
  13. 1 3
      src/pages/Preloader.jsx
  14. 7 6
      src/components/header/UserAvatar.js
  15. 0 0
      src/components/editPost/EditDescriptionPost.jsx
  16. 24 8
      src/components/uploadPhoto/EditPhotos.js
  17. 0 0
      src/components/editPost/EditTitlePost.jsx
  18. 13 13
      src/components/header/Header.jsx
  19. 9 9
      src/components/header/Search.jsx
  20. 0 11
      src/components/main/DateCreated.js
  21. 0 36
      src/components/main/Posts.jsx
  22. 0 73
      src/components/main/postsFeed/PostImage.jsx
  23. 0 67
      src/components/main/postsFeed/PostUserPanel.jsx
  24. 0 62
      src/components/main/profilePage/ModalFollow.js
  25. 59 0
      src/components/post/CommentsPostInTape.jsx
  26. 44 0
      src/components/post/PostDescription.jsx
  27. 20 9
      src/components/main/post/PostTitle.js
  28. 107 0
      src/components/post/PostImageCover.jsx
  29. 38 43
      src/components/main/post/PostComment.js
  30. 97 0
      src/components/post/PostUserPanel.jsx
  31. 1 51
      src/helpers/index.js
  32. 20 0
      src/hoc/RRoute.js
  33. BIN
      src/images/preloader.gif
  34. 10 9
      src/pages/AllPosts.jsx
  35. 1 0
      src/pages/Authorization.jsx
  36. 12 8
      src/pages/CollectionPage.jsx
  37. 30 30
      src/pages/EntityEditorPost.jsx
  38. 0 129
      src/pages/MainPostsFeed.jsx
  39. 57 0
      src/pages/PostOnePage.jsx
  40. 0 57
      src/pages/PostPage.jsx
  41. 77 0
      src/pages/PostsTapeMyFollowing.jsx
  42. 119 71
      src/pages/ProfilePage.jsx
  43. 48 23
      src/pages/SettingsPage.jsx
  44. 18 0
      src/redux/reducers/aboutMe-reducer.js
  45. 10 1
      src/redux/reducers/auth-reducer.js
  46. 0 22
      src/redux/reducers/myProfile-reducer.js
  47. 0 96
      src/redux/reducers/post-reducer.js
  48. 53 0
      src/redux/reducers/postOne-reducer.js
  49. 40 0
      src/redux/reducers/postsTape-reducer.js
  50. 14 0
      src/redux/reducers/profileData-reducer.js
  51. 0 5
      src/redux/reducers/route-reducer.js
  52. 13 9
      src/redux/redux-store.js
  53. 143 158
      src/redux/saga/index.js

+ 29 - 16
src/App.js

@@ -4,23 +4,31 @@ import { Router, Route, Switch, Redirect } from 'react-router-dom';
 import createHistory from "history/createBrowserHistory";
 import { connect, Provider } from 'react-redux';
 import store from './redux/redux-store';
-import { Content, Main } from './pages/Content';
-import { CProfilePage } from './pages/ProfilePage';
-import HeaderComponent from './components/header/Header';
-import { CMainPostsFeed } from './pages/MainPostsFeed';
-import { CRRoute } from './helpers';
-import { CPostPage } from './pages/PostPage';
-import { CAllPosts } from './pages/AllPosts';
-import { CEntityEditorPost } from './pages/EntityEditorPost';
-import { CSettingsPage } from './pages/SettingsPage';
 import { Authorization } from './pages/Authorization';
+import { CPostsTapeMyFollowing } from './pages/PostsTapeMyFollowing';
+import HeaderComponent from './components/header/Header';
+import { CProfilePage } from './pages/ProfilePage';
+import { CAllPostsTape } from './pages/AllPostsTape';
 import { CCollectionPage } from './pages/CollectionPage';
+import { CSettingsPage } from './pages/SettingsPage';
+import { CEditPostPage } from './pages/EditPostPage';
+import { CPostOnePage } from './pages/PostOnePage';
+import { CRRoute } from './hoc/RRoute';
+import { FooterComponet } from './components/FooterComponent';
 import { useMediaQuery } from 'react-responsive';
-import { FooterComponent } from './components/Footer';
+
 
 export const history = createHistory()
 
 
+const Main = ({ children }) =>
+    <div className='Main'>{children}</div>
+
+
+const Content = ({ children }) =>
+    <>{children}</>
+
+
 const AppContent = ({ isToken }) => {
     const isTabletDevice = useMediaQuery({
         query: "(max-width: 786px)"
@@ -38,24 +46,29 @@ const AppContent = ({ isToken }) => {
                 <HeaderComponent />
                 <Main>
                     <Switch>
-                        <Route path='/feed' component={CMainPostsFeed} />
+                        <Route path='/tape' component={CPostsTapeMyFollowing} />
                         <Route path='/profile/:_id' component={CProfilePage} />
-                        <Route path='/edit/post/:_id' component={CEntityEditorPost} />
+                        <Route path='/edit/post/:_id' component={CEditPostPage} />
                         <Route path='/my-settings' component={CSettingsPage} />
-                        <Route path='/all' component={CAllPosts} />
+                        <Route path='/all' component={CAllPostsTape} />
                         <Route path='/my-collection' component={CCollectionPage} />
-                        <CRRoute path='/post/:id' component={CPostPage} />
-                        <Redirect from='/*' to='/feed' />
+                        <CRRoute path='/post/:id' component={CPostOnePage} />
+                        <Redirect from='/*' to='/tape' />
                     </Switch>
                 </Main>
-                {isTabletDevice && <FooterComponent />}
+                {isTabletDevice && <FooterComponet  />}
             </Content >
         }
     </Router >
 }
 
+
 const CAppContent = connect(state => ({ isToken: state.auth?.token }))(AppContent)
 
+
+store.subscribe(() => console.log(store.getState()))
+
+
 function App() {
     return (
         <Provider store={store}>

+ 7 - 2
src/App.scss

@@ -429,13 +429,13 @@ video {
             max-width: 100%;
             height: 100%;
             margin: 0 auto;
-            object-fit:cover;
+            object-fit: cover;
         }
     }
     &__description {
         grid-area: 2 / 2 / 3 / 3;
         background-color: $defaultColorW;
-        padding: 5px 20px;
+        padding: 5px 5px 5px 20px;
         height: 100%;
         .ant-typography {
             overflow-wrap: anywhere;
@@ -458,6 +458,8 @@ video {
     &__comments {
         max-height: 300px;
         overflow: auto;
+        display: flex;
+        flex-direction: column-reverse;
     }
     &__comment-edit {
         display: block;
@@ -483,6 +485,9 @@ video {
     }
     .ant-empty-image {
         height: auto;
+        img {
+            margin: 0 auto;
+        }
     }
 }
 

+ 87 - 133
src/actions/index.js

@@ -1,32 +1,41 @@
-import { gql } from "../helpers";
-
+import { actionPromise } from "./actonsCreators"
+
+const getGQL = url =>
+    async (query, variables = {}) => {
+        let obj = await fetch(url, {
+            method: 'POST',
+            headers: {
+                "Content-Type": "application/json",
+                ...(localStorage.authToken
+                    ? { Authorization: 'Bearer ' + localStorage.authToken }
+                    : sessionStorage.authToken
+                        ? { Authorization: 'Bearer ' + sessionStorage.authToken }
+                        : {})
+            },
+            body: JSON.stringify({ query, variables })
+        })
+        let a = await obj.json()
+        if (!a.data && a.errors)
+            throw new Error(JSON.stringify(a.errors))
+        return a.data[Object.keys(a.data)[0]]
+    }
 
-//*************** ACTIONS PROMISE ******************//
+export const backURL = 'http://hipstagram.asmer.fs.a-level.com.ua'
 
+export const gql = getGQL(backURL + '/graphql');
 
-export const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
-export const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
-export const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
-export const actionClearPromise = (name) => ({ type: 'CLEAR-PROMISE', name })
-export const actionPromise = (name, promise) => ({ type: 'PROMISE_START', name, promise })
 
 
 //*************** ACTIONS AUTHORIZATION ******************//
 
 
-export const actionAuthLogin = (token, remember) => ({ type: 'AUTH-LOGIN', token, remember })
-export const actionAuthLogout = () => ({ type: 'AUTH-LOGOUT' })
-
 export const actionLogIn = (login, password) =>
     actionPromise('login', gql(`query login($login:String!, $password:String!){
                 login(login:$login, password:$password)
             }`, { login, password }))
 
-export const actionFullLogIn = (login, password, remember) => ({ type: 'FULL_LOGIN', login, password, remember })
-export const actionFullRegister = (login, password, remember) => ({ type: 'FULL_REGISTER', login, password, remember })
-
 export const actionRegister = (login, password) =>
-    actionPromise('register', gql(`mutation rega ($login:String!, $password:String!){
+    actionPromise('register', gql(`mutation rega ($login:String!,           $password:String!){
                                     createUser(login: $login, password: $password){
                                         _id login
                                     }
@@ -35,13 +44,8 @@ export const actionRegister = (login, password) =>
 
 //*************** Action ABOUT ME ******************//
 
-export const actionAboutMeAC = (data) => ({ type: 'ABOUTME-DATA-ADD', data })
-export const actionFullAboutMe = () => ({ type: 'ABOUT_ME' })
-export const actionRemoveMyDataAC = () => ({ type: 'REMOVE-MYDATA' })
-export const actionFullAboutMeUpsert = (nick, login) => ({ type: 'ABOUT_ME_UPSERT', nick, login })
 
-
-export const actionAboutMe = (id) =>
+export const actionGetAboutMe = (id) =>
     actionPromise('aboutMe', gql(`query userOned($myID:String!){
                         UserFindOne(query: $myID){
                             _id  login nick
@@ -53,21 +57,15 @@ export const actionAboutMe = (id) =>
 export const actionUpsertAboutMe = (myData) =>
     actionPromise('upsertAboutMe', gql(`mutation editAboutMe($user:UserInput){
                         UserUpsert(user:$user){
-                            _id   _id  login nick
-                            avatar { _id url }
-                            following{ _id}
+                            _id 
                         }
                 }`, { user: myData }))
 
 
-//*************** Action Posts Feed ******************//
+//*************** Action Posts Tape ******************//
 
 
-export const actionAddPostAC = (postsData, count) => ({ type: 'ADD-POSTS', newResult: postsData, count })
-export const actionRemovePostAC = () => ({ type: 'REMOVE-POSTS' })
-export const actionPostsFeed = () => ({ type: 'POSTS_FEED' })
-
-export const actionPostsMyFollowing = (skip, myFollowing) =>
+export const actionGetPostsMyFollowing = (skip, myFollowing) =>
     actionPromise('followingPosts',
         gql(`query allposts($query: String!){
             PostFind(query:$query){
@@ -89,19 +87,16 @@ export const actionPostsMyFollowing = (skip, myFollowing) =>
             }])
         }))
 
-export const actionPostsCount = (_id) =>
+export const actionGetPostsTapeCount = (_id) =>
     actionPromise('userPostsCount', gql(` query userPostsCount($id:String!){
                 PostCount(query:$id)
                 }`, { id: JSON.stringify([{ ___owner: { $in: _id } }]) }))
 
 
-//*************** Action Posts Profile ******************//
-
+//*************** Action Profile Data ******************//
 
-export const actionProfileDataAC = (postsData, count, userData) => ({ type: 'ADD-PROFILE-DATA', newResult: postsData, count, userData })
-export const actionProfilePageData = (id) => ({ type: 'DATA_PROFILE', id })
 
-export const actionProfileData = (_id) =>
+export const actionGetProfileData = (_id) =>
     actionPromise('userOneData', gql(` query userOned($id:String!){
                         UserFindOne(query: $id){
                             _id  login nick
@@ -112,7 +107,7 @@ export const actionProfileData = (_id) =>
                 
             } `, { id: JSON.stringify([{ _id }]) }))
 
-export const actionProfilePagePost = (_id, skip) => actionPromise('userOneDataPosts', gql(` query userOned($id:String!){
+export const actionGetProfilePagePosts = (_id, skip) => actionPromise('profilePosts', gql(` query userOned($id:String!){
                     PostFind(query:$id){
                         _id   images{ url _id originalFileName }
                     }
@@ -131,8 +126,6 @@ export const actionProfilePagePost = (_id, skip) => actionPromise('userOneDataPo
 //****************---All FIND POSTS---*************************//
 
 
-export const actionAllPosts = () => ({ type: 'ALL_POSTS' })
-
 export const actionGetAllPosts = (skip) =>
     actionPromise('allPosts', gql(` query allPosts($id:String!){
                 PostFind(query:$id){
@@ -152,12 +145,10 @@ export const actionAllPostsCount = () =>
                 }`, { id: JSON.stringify([{}]) }))
 
 
-//****************---Action FindUsers ---*************************//
+//****************---Action getAllUsers ---*************************//
 
 
-export const actionSearchUsers = (value) => ({ type: 'SEARCH_USERS', value })
-
-export const actionLoadSearchUsers = (value) =>
+export const actionGetAllUsers = (value) =>
     actionPromise('findUsersAll', gql(`query findUsersAll($query:String!) {
                                 UserFind(query: $query) {
                                     _id login nick 
@@ -177,82 +168,78 @@ export const actionLoadSearchUsers = (value) =>
 //****************---Action Like Post ---*************************//
 
 
-export const actionAddLikePostAC = (findId, newResult) => ({ type: 'ADD-POST-LIKE', findId, newResult })
-export const actionRemoveLikePostAC = (findId, newResult) => ({ type: 'REMOVE-POST-LIKE', findId, newResult })
-export const actionLikePost = (postId) => ({ type: 'LIKE_POST', postId })
-export const actionDelLikePost = (likeId, postId) => ({ type: 'DEL_LIKE_POST', likeId, postId })
-
-export const actionAddLikePost = (_id) =>
-    actionPromise('likePost', gql(`mutation LikePost($like:LikeInput){
+export const actionOnLikePost = (_id) =>
+    actionPromise('onLikePost', gql(`mutation LikePost($like:LikeInput){
         LikeUpsert(like:$like){
             _id
         }
     }`, { like: { post: { _id } } }))
 
-export const actionRemoveLikePost = (_id) =>
-    actionPromise('removelikePost', gql(`mutation LikeRemove($like:LikeInput){
+export const actionDelLikePost = (_id) =>
+    actionPromise('delLikePost', gql(`mutation LikeRemove($like:LikeInput){
             LikeDelete(like:$like){
                 _id
             }
         }`, { like: { _id } }))
 
-export const actionMyLikePost = (findId) =>
-    actionPromise('myLikes', gql(`query likeFindPost ($id:String!){
+export const actionGetMyLikePosts = (findId) =>
+    actionPromise('getMyLikes', gql(`query likeFindPost ($id:String!){
         PostFindOne(query:$id){
         likes { _id owner {_id}}
         }
     }`, { id: JSON.stringify([{ _id: findId }]) }))
 
+export const actionGetPostUsersLiked = (_id) =>
+    actionPromise('usersPostLiked', gql(` query usersPostLiked($id:String!) {
+                                        LikeFind(query:$id){
+                                          owner { _id nick login
+                                                avatar{_id url}
+                                    }
+                }
+            } `, { id: JSON.stringify([{ "post._id": _id }]) }))
 
-//****************---Action Like Comment ---*************************//
 
+//****************---Action Like Comment ---*************************//
 
-export const actionUpsertLikeCommentAC = (findId, newResult) => ({ type: 'UPSERT-LIKE-COMMENT', findId, newResult })
-export const actionLikeComment = (commentId) => ({ type: 'LIKE_COMMENT', commentId })
-export const actionDelLikeComment = (likeId, commentId) => ({ type: 'DEL_LIKE_COMMENT', likeId, commentId })
 
-export const actionAddLikeComment = (_id) =>
+export const actionOnLikeComment = (_id) =>
     actionPromise('likePost', gql(`mutation LikePost($like:LikeInput){
         LikeUpsert(like:$like){
             _id
         }
     }`, { like: { comment: { _id } } }))
 
-export const actionRemoveLikeComment = (_id) =>
+export const actionDelLikeComment = (_id) =>
     actionPromise('removelikeComment', gql(`mutation LikeRemove($like:LikeInput){
             LikeDelete(like:$like){_id}
         }`, { like: { _id } }))
 
-export const actionFindLikeComment = (findId) =>
+export const actionGetLikeComment = (commentId) =>
     actionPromise('findLikeComment', gql(`query findLikeComment ($id:String!){
         CommentFindOne(query:$id){
         likes { _id owner {_id}}
         }
-    }`, { id: JSON.stringify([{ _id: findId }]) }))
+    }`, { id: JSON.stringify([{ _id: commentId }]) }))
 
 
 //****************---Collection---*************************//
 
 
-export const actionUpsertCollectionAC = (data) => ({ type: 'UPSERT-COLLECTION', data })
-export const actionHandlerUpsertCollection = (_id, flag) => ({ type: 'HANDLER_UPSERT_COLLECTION', _id, flag })
-export const actionFullMyCollectionLoad = () => ({ type: 'LOAD_COLLECTION' })
+export const actionGetMyCollection = (_id) =>
+    actionPromise('getMyCollection', gql(`query getCollections($id:String! ){
+        CollectionFindOne(query:$id){
+        _id posts{ _id }
+        }
+    }`, { id: JSON.stringify([{ ___owner: _id }]) }))
 
-export const actionAddPostInCollections = (collectionId, newCollection) =>
+export const actionUpsertPostsInCollections = (collectionId, newCollection) =>
     actionPromise('addInCollections', gql(`mutation addInCollections($collection:CollectionInput ){
         CollectionUpsert(collection:$collection){
             _id 
         }
     }`, { collection: { _id: collectionId, posts: newCollection } }))
 
-export const actionFindMyCollections = (_id) =>
-    actionPromise('findMyCollections', gql(`query findCollections($id:String! ){
-        CollectionFindOne(query:$id){
-        _id text posts{_id }
-        }
-    }`, { id: JSON.stringify([{ ___owner: _id }]) }))
-
-export const actionOnLoadMyCollection = (_id, skip) =>
+export const actionGetPostsMyCollection = (_id, skip) =>
     actionPromise('onLoadMyCollections', gql(`query loadCollections($id:String! ){
        CollectionFind(query:$id){
          posts { _id  images{ _id url originalFileName}}
@@ -268,29 +255,21 @@ export const actionOnLoadMyCollection = (_id, skip) =>
 
 //****************---Action Subscribe ---*************************//
 
-
-export const actionUpdateMyFollowingAC = (data) => ({ type: 'UPDATE-MY-FOLLOWING', data })
-export const actionUpdateFollowersAC = (newResult) => ({ type: 'UPDATE-FOLLOWERS', newResult })
-
-export const actionSubscribe = (userId) => ({ type: 'SUBSCRIBE', userId })
-export const actionUnSubscribe = (userId) => ({ type: 'UN_SUBSCRIBE', userId })
-
-export const actionChangeSubscribe = (newFollowing) =>
+export const actionChangeSubscribed = (newFollowing) =>
     actionPromise('subscribe', gql(`mutation following($user:UserInput){
         UserUpsert( user:$user){
             following{_id}
         }
       }`, { user: newFollowing }))
 
-
-export const actionUpdateMyFollowing = (_id) =>
+export const actiongetMyFollowing = (_id) =>
     actionPromise('upDateFollowing', gql(` query followers($id:String!){
         UserFindOne(query: $id){
                             following {_id}
         }
     }`, { id: JSON.stringify([{ _id }]) }))
 
-export const actionUpdateFollowers = (_id) =>
+export const actionGetUserFollowers = (_id) =>
     actionPromise('upDateFollowers', gql(` query followers($id:String!){
         UserFindOne(query: $id){
                             followers {_id nick login}
@@ -301,14 +280,6 @@ export const actionUpdateFollowers = (_id) =>
 //****************---Action Comments ---*************************//
 
 
-export const actionAddCommentAC = (findId, newResult) => ({ type: 'ADD-COMMENT', findId, newResult })
-export const actionUpdateSubCommentAC = (findId, newResult) => ({ type: 'UPDATE-SUBCOMMENT', findId, newResult })
-export const actionEditCommentAC = (findId, newResult) => ({ type: 'EDIT-COMMENT', findId, newResult })
-export const actionEditComment = (commentId, text) => ({ type: 'COMMENT_EDIT', commentId, text })
-export const actionFullAddComment = (postId, text) => ({ type: 'COMMENT_POST', postId, text })
-export const actionAddSubComment = (commentId, text) => ({ type: 'ADD_SUB_COMMENT', commentId, text })
-export const actionSubComment = (commentId) => ({ type: 'FIND_SUBCOMMENT', commentId })
-
 export const actionAddComment = (postId, text) =>
     actionPromise('addcomment', gql(`mutation addcomment($comment: CommentInput ){
         CommentUpsert(comment:$comment){
@@ -316,8 +287,8 @@ export const actionAddComment = (postId, text) =>
         }
     }`, { comment: { post: { _id: postId }, text } }))
 
-export const actionFindComment = (findId) =>
-    actionPromise('findCommentPost', gql(`query commentFindPost ($id:String!){
+export const actionGetCommentPost = (findId) =>
+    actionPromise('commentPost', gql(`query commentFindPost ($id:String!){
         PostFindOne(query:$id){
             comments {
                 _id text createdAt 
@@ -332,23 +303,23 @@ export const actionFindComment = (findId) =>
         }
     }`, { id: JSON.stringify([{ _id: findId }]) }))
 
-export const actionFindSubComment = (findId) =>
+export const actionGetSubComment = (commentId) =>
     actionPromise('subComments', gql(`query commentFindOne ($id:String!){
-        CommentFindOne(query:$id){
-       _id text answers { 
-                _id text
-                post {_id }
-                answers { _id}
-                createdAt
-                likes { _id owner { _id login nick } }
-                owner {
-                    _id login nick 
-                    avatar { url } 
-                    } 
-                }
-        } 
-    }`, {
-        id: JSON.stringify([{ _id: findId }])
+                                CommentFindOne(query:$id){
+                                _id text answers { 
+                                        _id text
+                                        post {_id }
+                                        answers { _id}
+                                        createdAt
+                                        likes { _id owner { _id login nick } }
+                                        owner {
+                                            _id login nick 
+                                            avatar { url } 
+                                            } 
+                                        }
+                                } 
+                            }`, {
+        id: JSON.stringify([{ _id: commentId }])
     }))
 
 export const actionSubAddComment = (commentId, text) =>
@@ -358,7 +329,7 @@ export const actionSubAddComment = (commentId, text) =>
         }
     }`, { comment: { answerTo: { _id: commentId }, text } }))
 
-export const actionFindCommentText = (findId) =>
+export const actionGetCommentOne = (findId) =>
     actionPromise('subComments', gql(`query commentFindOne ($id:String!){
         CommentFindOne(query:$id){
        _id text 
@@ -367,7 +338,7 @@ export const actionFindCommentText = (findId) =>
         id: JSON.stringify([{ _id: findId }])
     }))
 
-export const actionUpsertEditComment = (commentId, text) =>
+export const actionEditComment = (commentId, text) =>
     actionPromise('editcomment', gql(`mutation addSubcomment($comment: CommentInput ){
         CommentUpsert(comment:$comment){
             _id text
@@ -378,9 +349,6 @@ export const actionUpsertEditComment = (commentId, text) =>
 //****************---Action Udate Avatar ---*************************//
 
 
-export const actionUpdateAvatar = (file) => ({ type: 'UPDATE_AVATAR', file })
-export const actionUpdateMyAvatart = (data) => ({ type: 'ABOUTME-UPDATE-AVATAR', data })
-
 export const actionSetAvatar = (file, id) =>
     actionPromise('uploadPhoto', gql(`mutation avaUpsert($ava: UserInput){
                 UserUpsert(user: $ava){
@@ -397,11 +365,8 @@ export const actionGetAvatar = (id) =>
     }`, { myID: JSON.stringify([{ _id: id }]) }))
 
 
-//****************--- Find FOllowing/Follovwrs---*************************//
+//****************--- Get user FOllowing/Follovwrs---*************************//
 
-export const actionFindFollowing = (_id) => ({ type: 'FIND_FOLLOWING', _id })
-export const actionFindFollowers = (_id) => ({ type: 'FIND_FOLLOWERS', _id })
-export const actionFindLiked = (_id) => ({ type: 'FIND_LIKED', _id })
 
 export const actionGetFindFollowing = (_id) =>
     actionPromise('findFollow', gql(` query findFollowing($id:String!){
@@ -425,22 +390,11 @@ export const actionGetFindFollowers = (_id) =>
                 }
             } `, { id: JSON.stringify([{ _id }]) }))
 
-export const actionGetFindLiked = (_id) =>
-    actionPromise('findLiked', gql(` query LikeFindPost($id:String!) {
-                                        LikeFind(query:$id){
-                                          owner { _id nick login
-                                                avatar{_id url}
-                                    }
-                }
-            } `, { id: JSON.stringify([{ "post._id": _id }]) }))
-
-
-//****************---Create Post ---*************************/
 
+//****************--- Create / Edit Post ---*************************/
 
-export const actionFullSentPost = (images, title, text) => ({ type: 'CREATE_POST', images, text, title })
 
-export const actionUpsertPost = (upSertPostObj) =>
+export const actionCreatOrEditPost = (upSertPostObj) =>
     actionPromise('sentPost', gql(`mutation sentPost($post: PostInput){
               PostUpsert(post: $post){
                     _id images{_id url originalFileName}

+ 138 - 0
src/actions/actonsCreators.js

@@ -0,0 +1,138 @@
+//*************** ACTIONS PROMISE ******************//
+
+export const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
+export const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
+export const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
+export const actionClearPromise = (name) => ({ type: 'CLEAR-PROMISE', name })
+export const actionPromise = (name, promise) => ({ type: 'PROMISE_START', name, promise })
+
+
+//*************** ActionCreator Authorization ******************//
+
+
+export const actionAuthLoginAC = (token, remember) => ({ type: 'AUTH-LOGIN', token, remember })
+export const actionAuthLogoutAC = () => ({ type: 'AUTH-LOGOUT' })
+
+export const actionFullLogInSagaAC = (login, password, remember) => ({ type: 'FULL_LOGIN', login, password, remember })
+export const actionFullRegisterSagaAC = (login, password, remember) => ({ type: 'FULL_REGISTER', login, password, remember })
+
+
+//*************** ActionCreator ABOUT ME ******************//
+
+
+export const actionAboutMeAC = (data) => ({ type: 'ABOUTME-DATA-ADD', data })
+export const actionClearAboutMeDataAC = () => ({ type: 'CLEAR-ABOUTME' })
+
+export const actionAboutMeSagaAC = () => ({ type: 'ABOUT_ME' })
+export const actionAboutMeUpsertSagaAC = (nick, login) => ({ type: 'ABOUT_ME_UPSERT', nick, login })
+
+
+//*************** ActionCreator TAPE POSTS ******************//
+
+
+export const actionAddPostsInPostsTapeAC = (newResult, count) => ({ type: 'POSTS-TAPE', newResult, count })
+export const actionClearPostsTapeAC = () => ({ type: 'CLEAR-POSTS-TAPE' })
+export const actionGetPostsTapeSagaAC = () => ({ type: 'GET_POSTS_TAPE' })
+
+
+//*************** ActionCreator POST ONE ******************//
+
+
+export const actionPostOneDataAC = (newResult) => ({ type: 'POST-ONE-DATA', newResult })
+
+export const actionClearPostsOneAC = () => ({ type: 'CLEAR-POST-ONE' })
+
+
+//*************** ActionCreator LIKE POSTS ******************//
+
+
+export const actionChangeStatusLikePostTapeAC = (postId, newResult) => ({ type: 'POSTS-TAPE-LIKE', postId, newResult })
+
+export const actionChangeLikePostTapeSagaAC = (postId, likeId) => ({ type: 'CHANGE_LIKE_POST_TAPE', postId, likeId })
+
+
+export const actionChangeStatusLikePostOneAC = (postId, newResult) => ({ type: 'POST-ONE-LIKE', postId, newResult })
+
+export const actionChangeLikePostOneSagaAC = (postId, likeId) => ({ type: 'CHANGE_LIKE_POST_ONE', postId, likeId })
+
+
+//**************** ActionCreator Collection *********************//
+
+
+export const actionUpsertCollectionAC = (data) => ({ type: 'UPSERT-COLLECTION', data })
+export const actionHandlerUpsertCollectionSagaAC = (_id, flag) => ({ type: 'HANDLER_UPSERT_COLLECTION', _id, flag })
+
+export const actionGetPostsMyCollectionSagaAC = () => ({ type: 'LOAD_COLLECTION' })
+
+
+//**************** ActionCreator Comments **********************//
+
+
+export const actionAddCommentPostInTapeAC = (postId, newResult) => ({ type: 'ADD-COMMENT-POST-TAPE', postId, newResult })
+
+export const actionAddCommentPostInTapeSagaAC = (postId, text) => ({ type: 'ADD_COMMENT_POST_TAPE', postId, text })
+
+
+export const actionAddCommentPostOneAC = (postId, newResult) => ({ type: 'POST-ONE-ADD-COMMENT', postId, newResult })
+
+export const actionAddCommentPostOneSagaAC = (postId, text) => ({ type: 'ADD_COMMENT_POST_ONE', postId, text })
+
+
+export const actionEditCommentAC = (commentId, newResult) => ({ type: 'EDIT-COMMENT', commentId, newResult })
+export const actionEditCommentSagaAC = (commentId, text) => ({ type: 'EDIT_COMMENT', commentId, text })
+
+
+export const actionUpdateCommentAC = (commentId, newResult) => ({ type: 'UPDATE-COMMENT', commentId, newResult })
+
+export const actionGetSubCommentSagaAC = (commentId) => ({ type: 'GET_SUBCOMMENT', commentId })
+
+export const actionAddSubComment = (commentId, text) => ({ type: 'ADD_SUB_COMMENT', commentId, text })
+
+
+//****************  ActionCreator Like Comment  *************************//
+
+
+export const actionLikeCommentAC = (commentId, newResult) => ({ type: 'LIKE-COMMENT', commentId, newResult })
+
+export const actionChangeLikeCommentSagaAC = (commentId, likeId) => ({ type: 'CHANGE_LIKE_COMMENT', commentId, likeId })
+
+
+//****************  ActionCreator FindUsers *************************//
+
+
+export const actionSearchAllUsers = (value) => ({ type: 'SEARCH_USERS', value })
+
+
+//****************  ActionCreator All Posts Users *************************//
+
+
+export const actionGetAllPostsSagaAC = () => ({ type: 'ALL_POSTS' })
+
+
+//****************  ActionCreator Profile User **************//
+
+
+export const actionProfileDataAC = (data) => ({ type: 'PROFILE-DATA', data })
+export const actionClearProfileDataAC = () => ({ type: 'CLEAR-PROFILE-DATA' })
+
+export const actionProfileDataSagaAC = (id) => ({ type: 'DATA_PROFILE', id })
+
+
+//****************  Action Subscribe *****************//
+
+export const actionUpdateMyFollowingAC = (data) => ({ type: 'UPDATE-MY-FOLLOWING', data })
+export const actionUpdateUserFollowersAC = (data) => ({ type: 'UPDATE-USER-FOLLOWERS', data })
+
+export const actionChangeSubscribeSagaAC = (userId, followCheck) => ({ type: 'CHANGE_SUBSCRIBE', userId, followCheck })
+
+
+//****************  Action Change Avatar *****************//
+
+export const actionChangeMyAvatartAC = (data) => ({ type: 'CHANGE-ABOUTME-AVATAR', data })
+export const actionChangeAvatarSagaAC = (file) => ({ type: 'UPDATE_AVATAR', file })
+
+
+//****************  Action Change Avatar *****************//
+
+
+export const actionCreatePostSagaAC = (images, title, text) => ({ type: 'CREATE_POST', images, text, title })

+ 6 - 6
src/components/main/profilePage/EditAvatar.js

@@ -1,13 +1,13 @@
-import { EditOutlined,} from '@ant-design/icons';
+import { EditOutlined, } from '@ant-design/icons';
 import { connect } from 'react-redux';
 import { Upload, message, } from 'antd';
-import {  propsUploadFile } from '../../../helpers';
-import { actionUpdateAvatar } from '../../../actions';
 import ImgCrop from 'antd-img-crop';
-import { UserAvatar } from '../../header/UserAvatar';
+import { actionChangeAvatarSagaAC } from '../actions/actonsCreators';
+import { propsUploadFile } from '../helpers';
+import { UserAvatar } from './UserAvatar';
 
 
-const EditAvatar = ({ avatar, onUploadFile }) => {
+const ChangeAvatar = ({ avatar, onUploadFile }) => {
     const handleChange = ({ file }) => {
         if (file.status === 'done') {
             message.success(`${file.name} file uploaded successfully`);
@@ -32,4 +32,4 @@ const EditAvatar = ({ avatar, onUploadFile }) => {
     )
 }
 
-export const CEditAvatar = connect(state => ({ avatar: state?.myData?.avatar }), { onUploadFile: actionUpdateAvatar })(EditAvatar)
+export const CChangeAvatar = connect(state => ({ avatar: state?.aboutMe?.avatar }), { onUploadFile: actionChangeAvatarSagaAC })(ChangeAvatar)

+ 24 - 25
src/components/main/postsFeed/FieldComment.jsx

@@ -4,10 +4,11 @@ import TextArea from 'antd/lib/input/TextArea'
 import { SendOutlined, } from '@ant-design/icons'
 import { connect } from 'react-redux'
 import { Button, Col, Row } from 'antd'
-import { actionAddSubComment, actionEditComment, actionFullAddComment } from '../../../actions'
+import { actionAddCommentPostInTapeSagaAC, actionAddCommentPostOneSagaAC, actionAddSubComment, actionEditCommentSagaAC } from '../actions/actonsCreators'
 
 
-const FieldCommentSend = ({ id, sentComment, autoFocus, value = '', setOpen, rows = 1,bordered=false }) => {
+
+const CommentField = ({ id, sentComment, autoFocus, value = '', setOpen, rows = 2, bordered = false }) => {
     const [commentValue, setCommentValue] = useState(value)
     const [error, setError] = useState(false)
 
@@ -15,30 +16,26 @@ const FieldCommentSend = ({ id, sentComment, autoFocus, value = '', setOpen, row
         setCommentValue(e.currentTarget.value)
         setError(false)
     }
-    const sendCommentValid = (value) => {
-        if (value.trim() !== '') {
-            sentComment(id, value.trim())
+
+    const sendCommentValid = () => {
+        if (commentValue.trim() !== '') {
+            sentComment(id, commentValue.trim())
             setCommentValue('')
+            setOpen(false)
         } else {
             setError(true)
         }
     }
 
-    const handlerClickBtn = () => {
-        sendCommentValid(commentValue)
-        setOpen(false)
-    }
-
-    const onKeyPressHandler = e => {
-        if (e.charCode === 13) {
-            sendCommentValid(commentValue)
-            setOpen(false)
-        }
-    }
-
     return (
-        <>
-            {error && <Text type='danger'>Field is required</Text>}
+        <div>
+            {error &&
+                <Text
+                    type='danger'
+                    style={{ display: 'inherit', textAlign: 'center' }}
+                >
+                    Field is required
+                </Text>}
             <Row align='middle' className='Post__send-comment'>
                 <Col flex='auto' offset={1}>
                     <TextArea value={commentValue}
@@ -47,23 +44,25 @@ const FieldCommentSend = ({ id, sentComment, autoFocus, value = '', setOpen, row
                         autoSize={{ minRows: 1, maxRows: rows }}
                         onChange={changeComentTextarea}
                         bordered={bordered}
-                        onKeyPress={onKeyPressHandler}
+                        maxLength={150}
                     />
                 </Col>
                 <Col span={2}>
                     <Button
-                        onClick={handlerClickBtn}
+                        onClick={sendCommentValid}
                         icon={< SendOutlined
                             style={{ fontSize: '1.2em', opacity: .6 }} />}
                     />
                 </Col>
             </Row>
-        </>
+        </div>
     )
 }
 
-export const CFieldCommentSend = connect(null, { sentComment: actionFullAddComment })(FieldCommentSend)
+export const CCommentFieldPostTape = connect(null, { sentComment: actionAddCommentPostInTapeSagaAC })(CommentField)
+
+export const CCommentFieldPostOne = connect(null, { sentComment: actionAddCommentPostOneSagaAC })(CommentField)
 
-export const CFieldSubCommentSend = connect(null, { sentComment: actionAddSubComment })(FieldCommentSend)
+export const CFieldSubComment = connect(null, { sentComment: actionAddSubComment })(CommentField)
 
-export const CFieldUpsertCommentSend = connect(null, { sentComment: actionEditComment })(FieldCommentSend)
+export const CFieldEditCommentSend = connect(null, { sentComment: actionEditCommentSagaAC})(CommentField)

+ 0 - 11
src/pages/Content.jsx

@@ -2,8 +2,6 @@ import { Col, Row } from 'antd'
 import React from 'react'
 
 
-
-
 export const Container = ({ children }) =>
     <Row justify='center' className='Main__inner'>
         <Col xs={{ span: 24 }} sm={{ span: 20 }} md={{ span: 16 }} lg={{ span: 14 }} xl={{ span: 12 }} >
@@ -12,12 +10,3 @@ export const Container = ({ children }) =>
     </Row>
 
 
-export const Main = ({ children }) =>
-    <div className='Main'>{children}</div>
-
-
-
-
-export const Content = ({ children }) =>
-    <>{children}</>
-

+ 0 - 11
src/components/Footer.jsx

@@ -1,11 +0,0 @@
-import { Footer } from 'antd/lib/layout/layout';
-import React from 'react';
-import { CUserNavIcon } from './header/Header';
-
-export const FooterComponent = () => {
-    return (
-        <Footer style={{ position: 'fixed', zIndex: 3, width: '100%', bottom: 0 }}>
-            <CUserNavIcon />
-        </Footer>
-    )
-};

+ 9 - 0
src/components/FooterComponent.jsx

@@ -0,0 +1,9 @@
+import { Footer } from 'antd/lib/layout/layout';
+import React from 'react';
+import { CUserNavIcon } from './header/Header';
+
+
+export const FooterComponet = () =>
+    <Footer style={{ position: 'fixed', zIndex: 3, width: '100%', bottom: 0 }}>
+        <CUserNavIcon />
+    </Footer>

+ 6 - 6
src/components/FormAuthorization.jsx

@@ -2,13 +2,14 @@ import React, { useEffect, useState } from 'react'
 import { connect } from 'react-redux'
 import { Form, Input, Button, Checkbox, message } from 'antd';
 import { UserOutlined, LockOutlined } from '@ant-design/icons';
-import { actionFullLogIn, actionFullRegister } from '../actions';
+import { actionFullLogInSagaAC, actionFullRegisterSagaAC } from '../actions/actonsCreators';
+
 
 const FormAuthorization = ({ buttonTitle, onSignIn, loginChek }) => {
     const [loading, setLoading] = useState(false);
     useEffect(() => {
         if (loginChek?.status === "PENDING") {
-            setLoading(loading => loading = true)
+            setLoading(true)
         }
         if (loginChek?.status === "RESOLVED" && loginChek?.payload === null) {
             message.error({
@@ -18,11 +19,10 @@ const FormAuthorization = ({ buttonTitle, onSignIn, loginChek }) => {
                     marginTop: '20vh',
                 },
             })
-            setLoading(loading => loading = false)
+            setLoading(false)
         }
     }, [loginChek?.status]);
 
-
     const onFinish = ({ login, password, remember }) => {
         onSignIn(login, password, remember)
     };
@@ -82,6 +82,6 @@ const FormAuthorization = ({ buttonTitle, onSignIn, loginChek }) => {
     )
 }
 
-export const CLoginForm = connect(state => ({ loginChek: state?.promise?.login || {} }), { onSignIn: actionFullLogIn, })(FormAuthorization)
+export const CLoginForm = connect(state => ({ loginChek: state?.promise?.login || {} }), { onSignIn: actionFullLogInSagaAC, })(FormAuthorization)
 
-export const CRegisterForm = connect(state => ({ status: state?.promise?.login }), { onSignIn: actionFullRegister, })(FormAuthorization)
+export const CRegisterForm = connect(state => ({ status: state?.promise?.login }), { onSignIn: actionFullRegisterSagaAC, })(FormAuthorization)

+ 55 - 0
src/components/GalleryMediaPostsUser.jsx

@@ -0,0 +1,55 @@
+import { connect } from "react-redux"
+import { Card, Col, Row } from 'antd'
+import postNoData from '../images/profile-post-no.jpeg'
+import { Link } from "react-router-dom"
+import { PlayCircleOutlined } from "@ant-design/icons"
+import { CircularGalleryIcon, videoRegExp } from "../helpers"
+import { backURL } from "../actions/actionsGetGql"
+
+
+const CardImage = ({ media }) =>
+    <div className='Profile__box' >
+        <img src={backURL + '/' + media?.images[0]?.url} alt="images post" />
+        {media?.images.length > 1 &&
+            < CircularGalleryIcon className='Profile__box-icon' style={{ stroke: 'black' }} />
+        }
+    </div>
+
+
+const CardVideo = ({ media }) =>
+    <div className='Profile__box' >
+        <video>
+            <source src={backURL + '/' + media?.images[0]?.url} />
+        </video>
+        <PlayCircleOutlined className='Profile__box-icon--video' />
+    </div>
+
+
+const ValidationImageOrVideo = ({ media }) => (
+    videoRegExp.test(media?.images[0]?.originalFileName)
+        ? <CardVideo media={media} />
+        : <CardImage media={media} />
+)
+
+
+const CardMediaPost = ({ media }) =>
+    <Link to={`/post/${media?._id}`}>
+        <Card className='Profile__post' hoverable>
+            {media?.images && media?.images[0] && media?.images[0]?.url
+                ? <ValidationImageOrVideo media={media} />
+                : <img src={postNoData} alt='no data' />
+            }
+        </Card>
+    </Link>
+
+
+const GalleryMediaPostsUser = ({ posts }) =>
+    <Row gutter={[15, 15]}>
+        {posts.map(p =>
+            <Col key={p._id} span={8}>
+                <CardMediaPost media={p} />
+            </Col>)
+        }
+    </Row >
+
+export const CGalleryMediaPostsUser = connect(state => ({ posts: state?.postsTape?.posts || [] }))(GalleryMediaPostsUser)

+ 75 - 0
src/components/ModalFollow.jsx

@@ -0,0 +1,75 @@
+import { Col, List, Row, Skeleton } from 'antd';
+import Modal from 'antd/lib/modal/Modal'
+import { useEffect } from 'react';
+import { connect } from 'react-redux';
+import { Link } from 'react-router-dom';
+import { actionGetFindFollowers, actionGetFindFollowing, actionGetPostUsersLiked } from '../actions/actionsGetGql';
+import { UserAvatar } from './UserAvatar';
+
+
+const ModalDataInner = ({ _id, avatar, nick, handleCancel }) =>
+    <Link to={`/profile/${_id}`} onClick={() => handleCancel()} style={{ width: '100%' }} >
+        <Row align='middle' >
+            <Col>
+                <UserAvatar avatar={avatar} avatarSize={'40px'} />
+            </Col>
+            <Col offset={2}>
+                {nick}
+            </Col>
+        </Row>
+    </Link >
+
+
+const ModalDataInfo = ({ id, status, statusModal, data, title, getData }) => {
+
+    const handleCancel = () => statusModal(false);
+    const newData = data.map(d => d.owner ? d.owner : d)
+
+    useEffect(() => {
+        getData(id)
+    }, [])
+
+
+    return (
+        <Modal className='Modal'
+            title={title}
+            visible={true}
+            destroyOnClose={true}
+            footer={null}
+            onCancel={handleCancel}>
+            {status !== 'RESOLVED'
+                ? <Skeleton className='Modal__inner' avatar active paragraph={{ rows: 0 }} />
+                : <List className='Modal__inner'
+                    itemLayout="horizontal"
+                    dataSource={newData}
+                    renderItem={item => (
+                        <List.Item >
+                            <ModalDataInner
+                                _id={item._id}
+                                avatar={item.avatar}
+                                nick={item.nick || item.login || 'No Name'}
+                                handleCancel={handleCancel} />
+                        </List.Item>
+                    )}
+                />}
+        </Modal>
+    )
+}
+
+export const CModalFollowers = connect(state => ({
+    id: state?.dataProfile?._id,
+    data: state?.promise?.findFollow?.payload?.followers || [],
+    status: state?.promise?.findFollow?.status
+}), { getData: actionGetFindFollowers })(ModalDataInfo)
+
+export const CModalFollowing = connect(state => ({
+    id: state?.dataProfile?._id,
+    data: state?.promise?.findFollow?.payload?.following || [],
+    status: state?.promise?.findFollow?.status
+}), { getData: actionGetFindFollowing })(ModalDataInfo)
+
+export const CModalUsersLiked = connect(state => ({
+    data: state?.promise?.usersPostLiked?.payload || [],
+    status: state?.promise?.usersPostLiked?.status
+}), { getData: actionGetPostUsersLiked })(ModalDataInfo)
+

+ 1 - 3
src/pages/Preloader.jsx

@@ -1,11 +1,9 @@
 import { message, Spin } from 'antd'
 import { connect } from 'react-redux'
-import preloader from '../images/preloader.gif'
 
 const PreloaderImg = () =>
     <div className='PreloaderImg'>
-{/* <Spin size="large" /> */}
-        <img src={preloader} alt="preloader" />
+        <Spin size="large" />
     </div>
 
 const Preloader = ({ promiseName, promiseState }) =>

+ 7 - 6
src/components/header/UserAvatar.js

@@ -1,12 +1,13 @@
 import Avatar from "antd/lib/avatar/avatar"
-import { backURL } from "../../helpers"
-import noAva from "../../images/noAva.png"
+import noAva from "../images/noAva.png"
+import { backURL } from "../actions/actionsGetGql"
 
 export const UserAvatar = ({ avatarSize, avatar }) =>
-    <Avatar style={{
-        width: avatarSize,
-        height: avatarSize
-    }}
+    <Avatar
+        style={{
+            width: avatarSize,
+            height: avatarSize
+        }}
         src={avatar && avatar?.url
             ? <img src={(backURL + '/' + avatar.url)} alt='avatar' />
             : <img src={noAva} alt="avatar" />

src/components/uploadPhoto/EditDescriptionPost.js → src/components/editPost/EditDescriptionPost.jsx


+ 24 - 8
src/components/uploadPhoto/EditPhotos.js

@@ -1,5 +1,5 @@
 import { DeleteOutlined, EyeOutlined, InboxOutlined } from "@ant-design/icons";
-import { Button, message, Image, Progress } from "antd";
+import { Button, message, Image, Progress, Upload } from "antd";
 import Dragger from "antd/lib/upload/Dragger";
 import React, { useState } from "react";
 import {
@@ -8,7 +8,8 @@ import {
     SortableElement,
     SortableHandle
 } from "react-sortable-hoc";
-import { backURL, propsUploadFile, videoRegExp } from "../../helpers";
+import { backURL } from "../../actions/actionsGetGql";
+import { propsUploadFile, videoRegExp } from "../../helpers";
 
 
 const SortableItemMask = ({ removePhotosItem, chekMedia, setVisible, id }) =>
@@ -79,17 +80,30 @@ const SortableList = SortableContainer(({ items = [], ...restProps }) => {
 export function EditPhotos({ photos = [], setPhotos }) {
     const [progress, setProgress] = useState(0);
     const [loading, setLoading] = useState(false);
-    const handlerChange = async ({ file, fileList }) => {
+    let count = []
+    const handlerChange = ({ file, fileList }) => {
         if (file.status === "uploading") {
             setLoading(true)
             setProgress(file.percent)
         } else if (file.status === 'done') {
+            console.log(file, fileList)
+            count = []
             message.success(`${file.name} file uploaded successfully`);
             setPhotos([...photos || [], file.response])
         } else if (file.status === 'error') {
             message.error(`${file.name} file upload failed.`);
         }
     }
+
+    const beforeUpload = (file) => {
+        if (count.length + photos.length > 8) {
+            message.error('Error, upload Max 8 elements')
+            return false
+        } else {
+            count = [...count, file]
+        }
+    }
+
     const removePhotosItem = (id) => setPhotos(photos.filter(p => p._id !== id))
     const onSortEnd = ({ oldIndex, newIndex }) => {
         setPhotos(arrayMove(photos, oldIndex, newIndex));
@@ -97,20 +111,22 @@ export function EditPhotos({ photos = [], setPhotos }) {
 
     return (
         <div className="EditPhotos" >
-            {photos?.length >= 8 ? null
-                : <Dragger {...propsUploadFile}
+            {photos.length < 8 &&
+                <Dragger {...propsUploadFile}
                     className="EditPhotos__box"
-                    multiple={true}
                     maxCount={8}
+                    beforeUpload={beforeUpload}
+                    multiple={true}
                     listType="picture-card"
                     showUploadList={false}
                     onChange={handlerChange}>
                     <p className="ant-upload-drag-icon">
                         <InboxOutlined />
                     </p>
-                    <p className="ant-upload-text">
+                    <div className="ant-upload-text">
                         Click or drag file to this area to upload
-                    </p>
+                        <p>{photos?.length} / 8</p>
+                    </div>
                 </Dragger>
             }
             {loading &&

src/components/uploadPhoto/EditTitlePost.js → src/components/editPost/EditTitlePost.jsx


+ 13 - 13
src/components/header/Header.jsx

@@ -6,15 +6,15 @@ import { connect } from 'react-redux';
 import { Header } from 'antd/lib/layout/layout';
 import { Col, Row } from 'antd';
 import { CompassOutlined, HomeOutlined, MessageOutlined, PlusCircleOutlined } from '@ant-design/icons/lib/icons';
-import { UserAvatar } from './UserAvatar';
 import MediaQuery from "react-responsive";
 import { CollectionEmptySvg } from '../../helpers';
+import { UserAvatar } from '../UserAvatar';
 
 
 const UserNavIcon = ({ userData: { _id, avatar, login } }) =>
     < Row justify="end" align="middle" className='Header__userNav' >
         <Col >
-            <NavLink to='/feed'><HomeOutlined /></NavLink>
+            <NavLink to='/tape'><HomeOutlined /></NavLink>
         </Col>
         <Col >
             <NavLink to='/direct'><MessageOutlined /></NavLink>
@@ -28,27 +28,27 @@ const UserNavIcon = ({ userData: { _id, avatar, login } }) =>
         <Col >
             <NavLink to='/my-collection'><CollectionEmptySvg /></NavLink>
         </Col>
-        <MediaQuery minWidth={787}>
-            <Col>
-                <Link to={`/profile/${_id}`}>
-                    <UserAvatar avatar={avatar} login={login} avatarSize={'45px'} />
-                </Link>
-            </Col>
-        </MediaQuery>
-        <MediaQuery maxWidth={786}>
+        {/* <MediaQuery minWidth={787}> */}
+        <Col>
+            <Link to={`/profile/${_id}`}>
+                <UserAvatar avatar={avatar} login={login} avatarSize={'45px'} />
+            </Link>
+        </Col>
+        {/* </MediaQuery> */}
+        {/* <MediaQuery maxWidth={786}>
             <Col>
                 <Link to={`/profile/${_id}`}>
                     <UserAvatar avatar={avatar} login={login} avatarSize={'45px'} />
                 </Link>
             </Col>
-        </MediaQuery>
+        </MediaQuery> */}
     </Row >
 
-export const CUserNavIcon = connect(state => ({ userData: state.myData || {} }))(UserNavIcon)
+export const CUserNavIcon = connect(state => ({ userData: state?.aboutMe || {} }))(UserNavIcon)
 
 
 const Logo = () =>
-    <Link className='Logo' to='/'>
+    <Link className='Logo' to='/tape'>
         <img src={logo} alt='logo' width='180vw' />
     </Link>
 

+ 9 - 9
src/components/header/Search.jsx

@@ -2,9 +2,9 @@ import { Empty, Input, Popover } from 'antd'
 import React from 'react'
 import { connect } from 'react-redux';
 import { Link } from 'react-router-dom';
-import {  actionSearchUsers } from '../../actions';
 import { SearchOutlined } from '@ant-design/icons';
-import { UserAvatar } from './UserAvatar';
+import { UserAvatar } from '../UserAvatar';
+import { actionSearchAllUsers } from '../../actions/actonsCreators';
 
 
 const FindUsersResult = ({ usersRes }) =>
@@ -12,20 +12,20 @@ const FindUsersResult = ({ usersRes }) =>
         {
             usersRes.length === 0 ?
                 <Empty /> :
-                usersRes.map(u => {
-                    return (<Link
+                usersRes.map(u =>
+                    <Link
                         className='Header__search-link'
                         key={u._id}
                         to={`/profile/${u._id}`} >
                         <UserAvatar avatar={u.avatar} login={u.login} nick={u.nick} avatarSize={'40px'} />
                         <strong>{u?.nick || u?.login || 'User'}</strong>
-                    </Link>)
-                })
+                    </Link>
+                )
         }
     </div >
 
 
-export const FieldSearch = ({ usersRes, findUsers }) =>
+export const FieldSearch = ({ usersRes, onGetAllUsers }) =>
     <>
         <Popover placement="bottom"
             content={<FindUsersResult usersRes={usersRes} />}
@@ -36,11 +36,11 @@ export const FieldSearch = ({ usersRes, findUsers }) =>
                 placeholder="Search users"
                 allowClear
                 prefix={<SearchOutlined style={{ color: '#c9c9c9' }} />}
-                onChange={e => findUsers(e.currentTarget.value)}
+                onChange={e => onGetAllUsers(e.currentTarget.value)}
             />
         </Popover>
     </>
 
 
 
-export const CFieldSearch = connect(state => ({ usersRes: state.promise?.findUsersAll?.payload || [] }), { findUsers: actionSearchUsers })(FieldSearch) 
+export const CFieldSearch = connect(state => ({ usersRes: state.promise?.findUsersAll?.payload || [] }), { onGetAllUsers: actionSearchAllUsers })(FieldSearch) 

+ 0 - 11
src/components/main/DateCreated.js

@@ -1,11 +0,0 @@
-import React from 'react'
-
-export const DateCreated = ({ date = '' }) => {
-    const newDate = new Date(date * 1)
-    const resultDate = new Intl.DateTimeFormat('default').format(newDate)
-    return (
-        <>
-            {resultDate}
-        </>
-    )
-}

+ 0 - 36
src/components/main/Posts.jsx

@@ -1,36 +0,0 @@
-import { connect } from "react-redux"
-import { Card, Col, Row } from 'antd'
-import postNoData from '../../images/profile-post-no.jpeg'
-import { Link } from "react-router-dom"
-import { backURL, CircularGalleryIcon, videoRegExp } from "../../helpers"
-import { PlayCircleOutlined } from "@ant-design/icons"
-
-const Posts = ({ posts }) =>
-    <Row gutter={[15, 15]}>
-        {Array.isArray(posts) && posts.map(p => <Col key={p._id} span={8}>
-            <Link to={`/post/${p._id}`}>
-                <Card className='Profile__post' hoverable>
-                    {p?.images && p?.images[0] && p.images[0]?.url
-                        ? videoRegExp.test(p.images[0]?.originalFileName)
-                            ? <div className='Profile__box' >
-                                <video>
-                                    <source src={backURL + '/' + p.images[0]?.url} />
-                                </video>
-                                <PlayCircleOutlined className='Profile__box-icon--video' />
-                            </div>
-                            : p.images.length === 1
-                                ?
-                                <img src={backURL + '/' + p.images[0]?.url} alt="images post" />
-                                : <div className='Profile__box' >
-                                    <CircularGalleryIcon className='Profile__box-icon' style={{ stroke: 'black' }} />
-                                    <img src={(backURL + '/' + p?.images[0]?.url)} alt='post Img' />
-                                </div>
-                        : <img src={postNoData} alt='no data' />
-                    }
-                </Card>
-            </Link>
-        </Col>)
-        }
-    </Row >
-    
-export const CPosts = connect(state => ({ posts: state.post?.posts || [] }))(Posts)

+ 0 - 73
src/components/main/postsFeed/PostImage.jsx

@@ -1,73 +0,0 @@
-import { LeftCircleOutlined, RightCircleOutlined } from "@ant-design/icons";
-import { Carousel, Empty } from "antd";
-import React, { createRef } from "react";
-import nodata from '../../../images/nodata.png'
-import { backURL, videoRegExp } from '../../../helpers/index'
-import MediaQuery from "react-responsive";
-
-class PostImage extends React.Component {
-    constructor(props) {
-        super(props);
-        this.carouselRef = createRef();
-        this.state = {
-            movePrev: false,
-            moveNext: false
-        }
-    }
-
-    handleNext = () => this.carouselRef.current.next(this);
-
-    handlePrev = () => this.carouselRef.current.prev(this);
-
-    moveOnDivArray = (length, index) => {
-        if (length === 1) {
-            this.setState({ movePrev: false, moveNext: false })
-        } else if (index === 0) {
-            this.setState({ movePrev: false, moveNext: true })
-        } else if (index === length - 1 && length > 1) {
-            this.setState({ movePrev: true, moveNext: false })
-        } else {
-            this.setState({ movePrev: true, moveNext: true })
-        }
-    }
-
-    downOnDivArray = () => this.setState({ movePrev: false, moveNext: false })
-
-    render() {
-
-        const { images } = this.props
-        return (
-            <Carousel ref={this.carouselRef}
-                effect="fade"
-                infinite={false}
-                dots={{ className: 'Post__dots' }
-                }>
-                {!!images ? images.map((i, index) => i?.url ? <div
-                    key={i._id}
-                    onMouseEnter={() => this.moveOnDivArray(images.length, index)}
-                    onMouseLeave={this.downOnDivArray}>
-                    <MediaQuery minWidth={768}>
-                        <button onClick={() => this.handlePrev()}
-                            className={`Post__prev Post__btn ${this.state.movePrev ? '--active' : ''}`}><LeftCircleOutlined /></button>
-                        <button onClick={() => this.handleNext()}
-                            className={`Post__next Post__btn ${this.state.moveNext ? '--active' : ''}`}><RightCircleOutlined /></button>
-                    </MediaQuery>
-                    <div className="PostOne__wrapper-img">
-                        {videoRegExp.test(i.originalFileName)
-                            ? <video controls loop preload="true" height="500">
-                                <source src={backURL + '/' + i.url} />
-                            </video>
-                            : <img src={backURL + '/' + i.url} />
-                        }
-                    </div>
-
-                </div>
-                    : <Empty key={i._id} image={nodata} description={false} />)
-                    : <Empty image={nodata} description={false} />
-                }
-            </Carousel >
-        );
-    }
-}
-
-export default PostImage

+ 0 - 67
src/components/main/postsFeed/PostUserPanel.jsx

@@ -1,67 +0,0 @@
-import { HeartFilled, HeartOutlined } from "@ant-design/icons"
-import { Button, Col, Row} from "antd"
-import { useState } from "react"
-import { connect } from "react-redux"
-import { actionDelLikePost, actionHandlerUpsertCollection, actionLikePost, } from "../../../actions"
-import { CollectionEmptySvg, CollectionSvgFill } from "../../../helpers"
-import { CModalPostLiked } from "../profilePage/ModalFollow"
-
-
-const HeartLike = ({ styleFontSize, likeStatus, changeLike }) =>
-    <Button
-        onClick={() => changeLike()}
-        type="none"
-        shape="circle"
-        icon={
-            likeStatus
-                ? <HeartFilled style={{ color: '#ff6969', fontSize: `${styleFontSize}` }} />
-                : <HeartOutlined style={{ color: '#1890ff', fontSize: `${styleFontSize}` }} />
-        }
-    />
-
-const PostUserPanel = ({ myID, postId = '', likes = [], collections, styleFontSize, addLikePost, removeLikePost, handlerCollection }) => {
-    const [open, setOpen] = useState(false)
-
-    const likeId = likes.find(l => l?.owner?._id === myID)?._id
-
-    const flag = collections?.posts.find(c => c._id === postId)
-
-    const changeLike = () => likeId ? removeLikePost(likeId, postId) : addLikePost(postId)
-
-    return (
-        <>
-            {open && <CModalPostLiked statusModal={setOpen} title={'Liked'} id={postId} />}
-            <Row className="Post__panel-btn" justify="space-between" align="middle">
-                <Col flex={'50%'}>
-                    <Row align="middle" wrap={false}>
-                        <Col className='Post__heart' >
-                            <HeartLike
-                                changeLike={changeLike}
-                                likeStatus={likeId}
-                                styleFontSize={styleFontSize} />
-                        </Col>
-                        <Col offset={1}>
-                            {!!likes.length && <button onClick={() => { setOpen(true) }}>Likes:<strong>{` ${likes.length}`}</strong></button>}
-                        </Col>
-                    </Row>
-                </Col>
-                <Col flex={'10%'} className='Post__collection'>
-                    <Button type="none"
-                        shape="circle" onClick={() => handlerCollection(postId, flag)}>
-                        {flag ? <CollectionSvgFill /> : <CollectionEmptySvg />}
-                    </Button>
-                </Col>
-            </Row>
-
-        </>
-    )
-}
-
-export const CPostUserPanel = connect(state => ({
-    myID: state.auth.payload.sub.id || '',
-    collections: state.myData?.collections
-}), {
-    addLikePost: actionLikePost,
-    removeLikePost: actionDelLikePost,
-    handlerCollection: actionHandlerUpsertCollection,
-})(PostUserPanel)

+ 0 - 62
src/components/main/profilePage/ModalFollow.js

@@ -1,62 +0,0 @@
-import { Col, List, Row, Skeleton } from 'antd';
-import Modal from 'antd/lib/modal/Modal'
-import { useEffect } from 'react';
-import { connect } from 'react-redux';
-import { Link } from 'react-router-dom';
-import { actionFindFollowers, actionFindFollowing, actionFindLiked } from '../../../actions';
-import { UserAvatar } from '../../header/UserAvatar';
-
-
-const ModalFolower = ({ id, status, statusModal, data, title, follow }) => {
-    const handleCancel = () => statusModal(false);
-
-    useEffect(() => {
-        follow(id)
-    }, [])
-    const newData = data.map(d => d.owner ? d.owner : d)
-    return (
-        <Modal className='Modal'
-            title={title}
-            visible={true}
-            destroyOnClose={true}
-            footer={null}
-            onCancel={handleCancel}>
-            {status !== 'RESOLVED'
-                ? <Skeleton className='Modal__inner' avatar active paragraph={{ rows: 0 }} />
-                : <List className='Modal__inner'
-                    itemLayout="horizontal"
-                    dataSource={newData}
-                    renderItem={item => (
-                        <List.Item >
-                            <Link to={`/profile/${item._id}`} style={{ width: '100%' }} onClick={handleCancel}>
-                                <Row align='middle' >
-                                    <Col>
-                                        <UserAvatar avatar={item.avatar} avatarSize={'40px'} />
-                                    </Col>
-                                    <Col offset={2}>{item.nick || item.login || 'No Name'}</Col>
-                                </Row>
-                            </Link>
-                        </List.Item>
-                    )}
-                />}
-        </Modal>
-    )
-}
-
-export const CModalFollowers = connect(state => ({
-    id: state?.post?.userData?._id,
-    data: state?.promise?.findFollow?.payload?.followers || [],
-    status: state?.promise?.findFollow?.status
-}), { follow: actionFindFollowers })(ModalFolower)
-
-export const CModalFollowing = connect(state => ({
-    id: state?.post?.userData?._id,
-    data: state?.promise?.findFollow?.payload?.following || [],
-    status: state?.promise?.findFollow?.status
-}), { follow: actionFindFollowing })(ModalFolower)
-
-export const CModalPostLiked = connect(state => ({
-    data: state?.promise?.findLiked?.payload || [],
-    status: state?.promise?.findLiked?.status
-}), { follow: actionFindLiked })(ModalFolower)
-

+ 59 - 0
src/components/post/CommentsPostInTape.jsx

@@ -0,0 +1,59 @@
+import { Divider, Tooltip } from "antd"
+import Paragraph from "antd/lib/typography/Paragraph"
+import { Link } from "react-router-dom"
+import moment from 'moment'
+import { CCommentFieldPostTape } from "../CommentField"
+
+
+export const PostCommentDate = ({ createdAt }) =>
+    <Tooltip
+        title={moment(new Date(+createdAt)).format('DD-MM-YYYY HH:mm:ss')}
+    >
+        {moment(new Date(+createdAt)).startOf('seconds').fromNow()}
+    </ Tooltip>
+
+
+const CommentPostInTape = ({ comment }) =>
+    <div className='CommentPostFeed'>
+        <Link to={`/profile/${comment.owner._id}`}>
+            {comment?.owner?.nick || comment?.owner?.login}
+        </Link>
+        <PostCommentDate createdAt={comment.createdAt} />
+        <Paragraph
+            style={{ paddingLeft: 20 }}
+            ellipsis={{ rows: 1, expandable: true, symbol: 'more' }}>
+            {comment?.text}
+        </Paragraph>
+    </div>
+
+
+const CommentsInnerPostInTape = ({ comments, _id }) =>
+    <>
+        {(comments.length > 2) && <Link to={`/post/${_id}`}>
+            <Divider orientation="left">
+                {`View ${comments.length} comments`}
+            </Divider>
+        </Link>}
+        {comments.slice(-2).map(c => <CommentPostInTape key={c._id} comment={c} />)}
+    </>
+
+
+const CommentsContentPostInTape = ({ comments, _id }) =>
+    <>
+        {
+            comments && comments.length
+                ? <CommentsInnerPostInTape comments={comments} _id={_id} />
+                : <Link to={`/post/${_id}`}>
+                    <Divider orientation="left">
+                        {comments?.length ? `View ${comments.length} comments` : 'No comments'}
+                    </Divider>
+                </Link>
+        }
+    </>
+
+
+export const CommentsPostInTape = ({ comments, _id }) =>
+    <>
+        <CommentsContentPostInTape comments={comments} _id={_id} />
+        <CCommentFieldPostTape id={_id} setOpen={() => { }} />
+    </>

+ 44 - 0
src/components/post/PostDescription.jsx

@@ -0,0 +1,44 @@
+import { Col, Row } from "antd"
+import Paragraph from "antd/lib/typography/Paragraph"
+import Text from "antd/lib/typography/Text"
+import { connect } from "react-redux"
+
+
+export const DateCreated = ({ date = '' }) => {
+
+    const newDate = new Date(date * 1)
+    const resultDate = new Intl.DateTimeFormat('default').format(newDate)
+
+    return (
+        <>
+            {resultDate}
+        </>
+    )
+}
+
+const PostTitle = ({ title, date }) =>
+    <Row justify='space-between'>
+        <Col >
+            {!!title && <Text level={3} strong>{title}</Text>}
+        </Col>
+        <Col >
+            <Text type='secondary'>
+                <DateCreated date={date} />
+            </Text>
+        </Col>
+    </Row>
+
+export const PostDescription = ({ title, description, date }) =>
+    <>
+        <PostTitle title={title} date={date} />
+        <Paragraph ellipsis={true ? { rows: 1, expandable: true, symbol: '...More' } : false}>
+            {description}
+        </Paragraph>
+    </>
+
+
+export const CPostDescription = connect(state => ({
+    title: state?.postOne?.title,
+    description: state?.postOne?.text,
+    date: state?.postOne?.createdAt
+}))(PostDescription) 

+ 20 - 9
src/components/main/post/PostTitle.js

@@ -2,7 +2,8 @@ import { Col, Row, Button, Dropdown, Menu } from 'antd'
 import { MoreOutlined } from '@ant-design/icons'
 import { Link } from 'react-router-dom'
 import { connect } from 'react-redux'
-import { UserAvatar } from '../../header/UserAvatar'
+import { UserAvatar } from '../UserAvatar'
+
 
 const MenuOverlay = ({ postId }) =>
     <Menu>
@@ -11,7 +12,22 @@ const MenuOverlay = ({ postId }) =>
         </Menu.Item>
     </Menu >
 
-export const PostTitle = ({ owner, myID, postId }) =>
+
+const EditMyPostBtn = ({ myID, postId, owner }) =>
+    <>
+        {owner?._id === myID &&
+            <Col>
+                <Dropdown overlay={<MenuOverlay postId={postId} />} placement="bottomRight" trigger={['click']}>
+                    <Button type='link'>
+                        <MoreOutlined style={{ fontSize: '1.4em' }} />
+                    </Button>
+                </Dropdown>
+            </Col>
+        }
+    </>
+export const CEditMyPostBtn = connect(state => ({ myID: state?.aboutMe?._id }))(EditMyPostBtn)
+
+export const PostHeader = ({ owner, children }) =>
     <Row justify="space-between" align='middle'>
         <Col>
             <Link to={`/profile/${owner?._id}`} className='owner'>
@@ -19,14 +35,9 @@ export const PostTitle = ({ owner, myID, postId }) =>
                 <span className='nick'>{owner?.nick ? owner.nick : owner?.login ? owner.login : 'Null'}</span>
             </Link >
         </Col>
-        {owner?._id === myID && <Col>
-            <Dropdown overlay={<MenuOverlay postId={postId} />} placement="bottomRight" trigger={['click']}>
-                <Button type='link'><MoreOutlined style={{ fontSize: '1.4em' }} /></Button>
-            </Dropdown>
-
-        </Col>}
+        {children}
     </Row>
 
 
 
-export const CPostTitle = connect(state => ({ myID: state?.auth?.payload?.sub?.id || '' }))(PostTitle)
+

+ 107 - 0
src/components/post/PostImageCover.jsx

@@ -0,0 +1,107 @@
+import { LeftCircleOutlined, RightCircleOutlined } from "@ant-design/icons";
+import { Carousel, Empty } from "antd";
+import React, { createRef } from "react";
+import nodata from '../../images/nodata.png'
+import MediaQuery from "react-responsive";
+import { backURL } from "../../actions/actionsGetGql";
+import { videoRegExp } from "../../helpers";
+
+
+const PostImageOrVideo = ({ url, originalFileName }) =>
+    <div className="PostOne__wrapper-img">
+        {
+            videoRegExp.test(originalFileName)
+                ? <video controls loop preload="true">
+                    <source src={backURL + '/' + url} />
+                </video>
+                : <img src={backURL + '/' + url} alt="post image" />
+        }
+    </div>
+
+
+const PostImageCaruselBtn = ({ styleClass, handlerClick, flagClass, children }) => 
+        <MediaQuery minWidth={768}>
+            <button onClick={() => handlerClick()}
+                className={`${styleClass} Post__btn ${flagClass ? '--active' : ''}`}>
+                {children}
+            </button>
+        </MediaQuery>
+
+
+class PostImage extends React.Component {
+    render() {
+        const { url, originalFileName, handlePrev, handleNext, movePrev, moveNext } = this.props
+        return (
+            <>
+                {url ? <>
+                    <PostImageCaruselBtn styleClass={'Post__prev'} handlerClick={handlePrev} flagClass={movePrev} >
+                        <LeftCircleOutlined />
+                    </PostImageCaruselBtn>
+                    <PostImageCaruselBtn styleClass={'Post__next'} handlerClick={handleNext} flagClass={moveNext} >
+                        <RightCircleOutlined />
+                    </PostImageCaruselBtn>
+                    <PostImageOrVideo url={url} originalFileName={originalFileName} />
+                </>
+                    : <Empty image={nodata} description={false} />}
+            </>)
+    }
+}
+
+class PostImageCover extends React.Component {
+    constructor(props) {
+        super(props);
+        this.carouselRef = createRef();
+        this.state = {
+            movePrev: false,
+            moveNext: false
+        }
+    }
+
+    handleNext = () => this.carouselRef.current.next(this);
+
+    handlePrev = () => this.carouselRef.current.prev(this);
+
+    moveOnDivArray = (length, index) => {
+        if (length === 1) {
+            this.setState({ movePrev: false, moveNext: false })
+        } else if (index === 0) {
+            this.setState({ movePrev: false, moveNext: true })
+        } else if (index === length - 1 && length > 1) {
+            this.setState({ movePrev: true, moveNext: false })
+        } else {
+            this.setState({ movePrev: true, moveNext: true })
+        }
+    }
+
+    downOnDivArray = () => this.setState({ movePrev: false, moveNext: false })
+
+    render() {
+
+        const { images } = this.props
+        return (
+            <Carousel ref={this.carouselRef}
+                effect="fade"
+                infinite={false}
+                dots={{ className: 'Post__dots' }
+                }>
+                {images
+                    ? images.map((i, index) => <div
+                        key={i._id}
+                        onMouseEnter={() => this.moveOnDivArray(images.length, index)}
+                        onMouseLeave={this.downOnDivArray}>
+                        <PostImage
+                            url={i.url}
+                            handleNext={this.handleNext}
+                            handlePrev={this.handlePrev}
+                            originalFileName={i.originalFileName}
+                            moveNext={this.state.moveNext}
+                            movePrev={this.state.movePrev} />
+                    </div>)
+                    : <Empty image={nodata} description={false} />
+                }
+            </Carousel >
+        );
+    }
+}
+
+export default PostImageCover

+ 38 - 43
src/components/main/post/PostComment.js

@@ -7,9 +7,11 @@ import moment from 'moment';
 import { Link } from 'react-router-dom';
 import { EditOutlined, LikeFilled, LikeOutlined, MoreOutlined } from '@ant-design/icons';
 import Paragraph from 'antd/lib/typography/Paragraph';
-import { UserAvatar } from '../../header/UserAvatar';
-import { CFieldSubCommentSend, CFieldUpsertCommentSend } from '../postsFeed/FieldComment';
-import { actionDelLikeComment, actionLikeComment, actionSubComment } from '../../../actions';
+import { UserAvatar } from '../UserAvatar';
+import { PostCommentDate } from './CommentsPostInTape';
+import { CFieldEditCommentSend, CFieldSubComment, CFieldUpsertCommentSend } from '../CommentField';
+import { actionChangeLikeCommentSagaAC, actionGetSubCommentSagaAC } from '../../actions/actonsCreators';
+
 
 
 
@@ -31,46 +33,46 @@ const PostCommentText = ({ myID, commentId, owner, text }) => {
     const [editComment, setEditComment] = useState(false)
     return (
         <>
-            {owner?._id === myID && <Dropdown overlay={<EditMenu setEditComment={setEditComment} />} placement="bottomRight">
-                <span className='PostOne__comment-edit'
-                >
-                    <MoreOutlined />
-                </span >
-            </Dropdown>}
+            {owner?._id === myID &&
+                <Dropdown
+                    overlay={<EditMenu setEditComment={setEditComment} />}
+                    placement="bottomRight">
+                    <span className='PostOne__comment-edit'>
+                        <MoreOutlined />
+                    </span>
+                </Dropdown>
+            }
             {!editComment
-                ? <Dropdown overlay={owner?._id === myID && <EditMenu setEditComment={setEditComment} />} trigger={['contextMenu']}>
+                ? <Dropdown
+                    overlay={owner?._id === myID && <EditMenu setEditComment={setEditComment} />}
+                    trigger={['contextMenu']}>
                     <Paragraph ellipsis={{ rows: 2, expandable: true, symbol: 'more' }} >
                         {text}
                     </ Paragraph>
                 </Dropdown>
-                : <CFieldUpsertCommentSend value={text} id={commentId} autoFocus={true} setOpen={setEditComment} rows={4} bordered={true} />}
+                : <CFieldEditCommentSend value={text} id={commentId} autoFocus={true} setOpen={setEditComment} rows={2} bordered={true} />}
         </>)
 }
 
 const CPostCommentText = connect(state => ({ myID: state.auth.payload.sub.id || '' }))(PostCommentText)
 
 
-export const PostCommentDate = ({ createdAt }) =>
-    <Tooltip title={moment(new Date(+createdAt)).format('DD-MM-YYYY HH:mm:ss')} >
-        {moment(new Date(+createdAt)).startOf().fromNow()}
-    </ Tooltip>
-
-
-const PostCommentAction = ({ myID, commentId, likes, delLikeComment, addLikeComment }) => {
+const PostCommentAction = ({ myID, commentId, likes, changeLikeComment }) => {
     const [open, setOpen] = useState(false);
-    debugger
     const likeId = likes.find(l => l?.owner?._id === myID)?._id
-    console.log(likeId)
-    const changeLike = () => likeId ? delLikeComment(likeId, commentId) : addLikeComment(commentId)
-
     return (
         <>
-            <span onClick={changeLike}>
+            <span onClick={() => changeLikeComment(commentId, likeId)}>
                 {likeId ? <LikeFilled /> : <LikeOutlined />}
-                <span style={{ paddingLeft: 8, cursor: 'auto' }}>{likes.length ? likes.length : ''}</span>
+                {
+                    !!likes.length &&
+                    <span style={{ paddingLeft: 8, cursor: 'auto' }}>
+                        {likes.length}
+                    </span>
+                }
             </span>
             <span onClick={() => setOpen(!open)}>Reply to</span>
-            {open && <CFieldSubCommentSend autoFocus={true} id={commentId} setOpen={setOpen} />}
+            {open && <CFieldSubComment autoFocus={true} id={commentId} setOpen={setOpen} />}
         </>
     )
 }
@@ -78,44 +80,37 @@ const PostCommentAction = ({ myID, commentId, likes, delLikeComment, addLikeComm
 const CPostCommentAction = connect(state => ({
     myID: state.auth.payload.sub.id || ''
 }), {
-    addLikeComment: actionLikeComment,
-    delLikeComment: actionDelLikeComment
+    changeLikeComment: actionChangeLikeCommentSagaAC
 })(PostCommentAction)
 
-const PostComments = ({ comments, findSubComment, parentId, }) => {
-    return (<>
+const PostOneComments = ({ comments, findSubComment, parentId, }) =>
+    <>
         {comments?.length && Object.keys(comments[0]).length > 1
             ? comments.map(c => {
                 return (
                     <Comment
                         key={c._id}
                         author={<PostCommentAuthor owner={c.owner} />}
-                        avatar={< UserAvatar avatar={c?.owner?.avatar} avatarSize={'35px'} />}
+                        avatar={<UserAvatar avatar={c?.owner?.avatar} avatarSize={'35px'} />}
                         datetime={<PostCommentDate createdAt={c.createdAt} />}
                         content={<CPostCommentText text={c.text} commentId={c._id} owner={c.owner} />}
                         actions={[<CPostCommentAction likes={c.likes} commentId={c._id} />]}
                     >
-                        {
-                            c.answers && c.answers?.length
-                                ? <>
-                                    <PostComments comments={c?.answers} parentId={c._id} findSubComment={findSubComment} />
-                                </>
-                                : null
-                        }
+                        <PostOneComments comments={c?.answers} parentId={c._id} findSubComment={findSubComment} />
                     </Comment>
                 )
             })
             :
-            !!comments.length && <Divider plain>
+            !!comments?.length && <Divider plain>
                 <Text type='secodary' onClick={() => findSubComment(parentId)} >
                     View answers {comments.length}
                 </Text>
             </Divider>
         }
-    </>)
-}
+    </>
+
 
-export const CPostComments = connect(state => ({
-    comments: state?.post?.posts?.comments || [],
+export const CPostOneComments = connect(state => ({
+    comments: state?.postOne?.comments || [],
 
-}), { findSubComment: actionSubComment })(PostComments)
+}), { findSubComment: actionGetSubCommentSagaAC })(PostOneComments)

+ 97 - 0
src/components/post/PostUserPanel.jsx

@@ -0,0 +1,97 @@
+import { HeartFilled, HeartOutlined } from "@ant-design/icons"
+import { Button, Col, Row } from "antd"
+import { useState } from "react"
+import { connect } from "react-redux"
+import { actionChangeLikePostOneSagaAC, actionChangeLikePostTapeSagaAC, actionDelLikePostSagaAC, actionHandlerUpsertCollectionSagaAC, actionOnLikePostSagaAC } from "../../actions/actonsCreators"
+import { CollectionEmptySvg, CollectionSvgFill } from "../../helpers"
+import { CModalUsersLiked } from "../ModalFollow"
+
+
+const HeartIcon = ({ myID, postId, styleFontSize, likes, changeLikePost }) => {
+    const likeId = likes.find(l => l?.owner?._id === myID)?._id
+    return (
+        <Button
+            onClick={() => changeLikePost(postId, likeId)}
+            type="none"
+            shape="circle"
+            icon={
+                likeId
+                    ? <HeartFilled style={{ color: '#ff6969', fontSize: `${styleFontSize}` }} />
+                    : <HeartOutlined style={{ color: '#1890ff', fontSize: `${styleFontSize}` }} />
+            }
+        />
+    )
+}
+
+const CHeartIcon = connect(state => ({ myID: state?.aboutMe?._id || '' }))(HeartIcon)
+
+
+
+const PostLike = ({ postId, likes = [], styleFontSize, changeLikePost }) => {
+
+    const [open, setOpen] = useState(false)
+
+    return (
+        <>
+            <Row align="middle" wrap={false}>
+                <Col className='Post__heart' >
+                    <CHeartIcon
+                        postId={postId}
+                        likes={likes}
+                        styleFontSize={styleFontSize}
+                        changeLikePost={changeLikePost}
+                    />
+                </Col>
+                <Col offset={1}>
+                    {!!likes.length &&
+                        <button onClick={() => { setOpen(true) }}>
+                            Likes:<strong>{` ${likes.length}`}</strong>
+                        </button>}
+                </Col>
+            </Row>
+            {open && <CModalUsersLiked id={postId} title={'Liked'} statusModal={setOpen} />}
+        </>)
+}
+
+
+const CollectionButton = ({ postId, collection, handlerCollection }) => {
+    const flag = collection?.find(c => c._id === postId)
+    return (
+        <Button
+            type="none"
+            shape="circle"
+            onClick={() => handlerCollection(postId, flag)}>
+            {flag ? <CollectionSvgFill /> : <CollectionEmptySvg />}
+        </ Button >
+    )
+}
+
+const CCollectionButton = connect(state => ({ collection: state?.aboutMe?.collection?.posts }))(CollectionButton)
+
+
+const PostUserPanel = ({ postId, likes, styleFontSize, changeLikePost, handlerCollection }) =>
+    <Row className="Post__panel-btn" justify="space-between" align="middle">
+        <Col flex={'50%'}>
+            <PostLike
+                postId={postId}
+                likes={likes}
+                styleFontSize={styleFontSize}
+                changeLikePost={changeLikePost}
+            />
+        </Col>
+        <Col flex={'10%'} className='Post__collection'>
+            <CCollectionButton
+                postId={postId}
+                handlerCollection={handlerCollection} />
+        </Col>
+    </Row>
+
+export const CPostTapeUserPanel = connect(null, {
+    handlerCollection: actionHandlerUpsertCollectionSagaAC,
+    changeLikePost: actionChangeLikePostTapeSagaAC,
+})(PostUserPanel)
+
+export const CPostOneUserPanel = connect(null, {
+    handlerCollection: actionHandlerUpsertCollectionSagaAC,
+    changeLikePost: actionChangeLikePostOneSagaAC,
+})(PostUserPanel)

+ 1 - 51
src/helpers/index.js

@@ -1,15 +1,8 @@
 import Icon from '@ant-design/icons';
-import { useEffect } from 'react';
-import { connect } from 'react-redux';
-import { Route } from 'react-router-dom';
-
-
-export const backURL = 'http://hipstagram.asmer.fs.a-level.com.ua'
-
+import { backURL } from '../actions/actionsGetGql';
 
 export const videoRegExp = (/\.(mp4|mov|avi|wmv|flv|flv|3gp|mpg)$/i)
 
-
 export const propsUploadFile = {
     name: 'photo',
     action: `${backURL}/upload`,
@@ -17,39 +10,6 @@ export const propsUploadFile = {
 }
 
 
-export const jwtDecode = (token) => {
-    try {
-        let arrToken = token.split('.')
-        let base64Token = atob(arrToken[1])
-        return JSON.parse(base64Token)
-    }
-    catch (e) {
-        console.log('Ой, ошибочка вышла ' + e);
-    }
-}
-
-const getGQL = url =>
-    async (query, variables = {}) => {
-        let obj = await fetch(url, {
-            method: 'POST',
-            headers: {
-                "Content-Type": "application/json",
-                ...(localStorage.authToken
-                    ? { Authorization: 'Bearer ' + localStorage.authToken }
-                    : sessionStorage.authToken
-                        ? { Authorization: 'Bearer ' + sessionStorage.authToken }
-                        : {})
-            },
-            body: JSON.stringify({ query, variables })
-        })
-        let a = await obj.json()
-        if (!a.data && a.errors)
-            throw new Error(JSON.stringify(a.errors))
-        return a.data[Object.keys(a.data)[0]]
-    }
-
-export const gql = getGQL(backURL + '/graphql');
-
 
 const CircularGallerySvg = () =>
     <svg aria-label="Кольцевая галерея" color="#ffffff" fill="#ffffff" height="22" role="img" viewBox="0 0 48 48" width="22">
@@ -74,15 +34,5 @@ export const CollectionSvgFill = props =>
     <Icon component={CollectionFillSvg} {...props} />
 
 
-const RRoute = ({ action, component: Component, ...routeProps }) => {
-    const WrapperComponent = (componentProps) => {
-        useEffect(() => {
-            action(componentProps.match)
-        })
-        return <Component {...componentProps} />
-    }
-    return <Route {...routeProps} component={WrapperComponent} />
-}
 
-export const CRRoute = connect(null, { action: match => ({ type: 'ROUTE', match }) })(RRoute)
 

+ 20 - 0
src/hoc/RRoute.js

@@ -0,0 +1,20 @@
+import { useEffect } from "react"
+import { connect } from "react-redux"
+import { Route } from "react-router-dom"
+
+
+
+const RRoute = ({ action, component: Component, ...routeProps }) => {
+    const WrapperComponent = (componentProps) => {
+        useEffect(() => {
+            action(componentProps.match)
+        })
+        return <Component {...componentProps} />
+    }
+    return <Route {...routeProps} component={WrapperComponent} />
+}
+
+export const CRRoute = connect(null, { action: match => ({ type: 'ROUTE', match }) })(RRoute)
+
+
+

BIN
src/images/preloader.gif


+ 10 - 9
src/pages/AllPosts.jsx

@@ -1,16 +1,17 @@
 import React, { useEffect, useState } from 'react';
 import { connect } from 'react-redux';
-import { actionAllPosts, actionRemovePostAC } from '../actions';
-import { CPosts } from '../components/main/Posts';
-import { Container } from './Content';
-import { CPreloader } from './Preloader';
+import { actionAllGetPostsSagaAC, actionClearPostsTapeAC, actionGetAllPostsSagaAC } from '../actions/actonsCreators';
+import { Container } from '../components/Container';
+import { CGalleryMediaPostsUser } from '../components/GalleryMediaPostsUser';
+import { CPreloader } from '../components/Preloader';
 
-const AllPosts = ({ onAllPosts, postsRemove }) => {
+
+const AllPostsTape = ({ onGetAllPosts, clearPostsTape }) => {
     const [checkScroll, setCheckScroll] = useState(true)
 
     useEffect(() => {
         if (checkScroll) {
-            onAllPosts()
+            onGetAllPosts()
             setCheckScroll(false)
         }
     }, [checkScroll])
@@ -19,7 +20,7 @@ const AllPosts = ({ onAllPosts, postsRemove }) => {
         document.addEventListener('scroll', scrollHandler)
         return () => {
             document.removeEventListener('scroll', scrollHandler)
-            postsRemove()
+            clearPostsTape()
         }
     }, [])
 
@@ -32,9 +33,9 @@ const AllPosts = ({ onAllPosts, postsRemove }) => {
     return (
         <Container>
             <CPreloader promiseName='allPosts' />
-            <CPosts />
+            <CGalleryMediaPostsUser />
         </Container>
     )
 }
 
-export const CAllPosts = connect(null, { onAllPosts: actionAllPosts, postsRemove: actionRemovePostAC, })(AllPosts)
+export const CAllPostsTape = connect(null, { onGetAllPosts: actionGetAllPostsSagaAC, clearPostsTape: actionClearPostsTapeAC })(AllPostsTape)

+ 1 - 0
src/pages/Authorization.jsx

@@ -1,6 +1,7 @@
 import { Card, Col, Divider, Row } from "antd";
 import { NavLink } from "react-router-dom";
 import { CLoginForm, CRegisterForm } from "../components/FormAuthorization";
+
 import authBg from "../images/authBg.png"
 
 export const Authorization = ({ match: { params: { _id } } }) =>

+ 12 - 8
src/pages/CollectionPage.jsx

@@ -2,17 +2,18 @@ import { Divider } from 'antd';
 import Title from 'antd/lib/typography/Title';
 import React, { useEffect } from 'react';
 import { connect } from 'react-redux';
-import { actionFullMyCollectionLoad, actionRemovePostAC } from '../actions';
-import { CPosts } from '../components/main/Posts';
-import { Container } from './Content';
-import { CPreloader } from './Preloader';
+import { actionClearPostsTapeAC, actionGetPostsMyCollectionSagaAC } from '../actions/actonsCreators';
+import { Container } from '../components/Container';
+import { CGalleryMediaPostsUser } from '../components/GalleryMediaPostsUser';
+import { CPreloader } from '../components/Preloader';
 
-export const CollectionPage = ({ posts, onLoadPosts, postsRemove }) => {
+
+export const CollectionPage = ({ onLoadPosts, clearPostsTape }) => {
 
     useEffect(() => {
         onLoadPosts()
         return () => {
-            postsRemove()
+            clearPostsTape()
         }
     }, [])
 
@@ -20,8 +21,11 @@ export const CollectionPage = ({ posts, onLoadPosts, postsRemove }) => {
         <Container>
             <CPreloader promiseName='onLoadMyCollections' />
             <Divider><Title level={1}>Collections</Title></Divider>
-            <CPosts />
+            <CGalleryMediaPostsUser />
         </Container>
     )
 }
-export const CCollectionPage = connect(state => ({ posts: state?.post?.posts || [] }), { onLoadPosts: actionFullMyCollectionLoad, postsRemove: actionRemovePostAC })(CollectionPage)
+export const CCollectionPage = connect(null, {
+    onLoadPosts: actionGetPostsMyCollectionSagaAC,
+    clearPostsTape: actionClearPostsTapeAC
+})(CollectionPage)

+ 30 - 30
src/pages/EntityEditorPost.jsx

@@ -2,41 +2,31 @@ import { Button, Divider, message } from "antd"
 import Title from "antd/lib/typography/Title"
 import { useEffect, useState } from "react"
 import { connect } from "react-redux"
-import { actionAddPostAC, actionClearPromise, actionFullSentPost, actionRemovePostAC, } from "../actions"
-import { EditPhotos } from "../components/uploadPhoto/EditPhotos"
-import { EditDescriptionPost } from "../components/uploadPhoto/EditDescriptionPost"
-import { EditTitlePost } from "../components/uploadPhoto/EditTitlePost"
-import { Container } from "./Content"
+import { actionClearPostsOneAC, actionCreatePostSagaAC, actionPostOneDataAC } from "../actions/actonsCreators"
 import { history } from '../App'
+import { Container } from "../components/Container"
+import { EditDescriptionPost } from "../components/editPost/EditDescriptionPost"
+import { EditPhotos } from "../components/editPost/EditPhotos"
+import { EditTitlePost } from "../components/editPost/EditTitlePost"
 
 
 const ContainEditorPost = ({ children }) =>
     <div className='ContainEditPost ContainerInner'>{children}</div>
 
-const EntityEditorPost = ({ match: { params: { _id } }, myID, entity, status, onSave, updatePost, clearState, clearStatus }) => {
+
+const EditPostPage = ({ match: { params: { _id } }, myID, entity, status, onSave, clearPostOne }) => {
 
     const [photos, setPhotos] = useState(entity?.images || []);
     const [titleSend, setTitleSend] = useState(entity?.title || '')
     const [description, setDescription] = useState(entity?.text || '');
 
     useEffect(() => {
-        if (_id !== 'new') {
-            if (Array.isArray(entity)) {
-                let findEntity = entity.find(e => e._id === _id)
-                setPhotos(findEntity?.images)
-                setTitleSend(findEntity?.title)
-                setDescription(findEntity?.text)
-                updatePost(findEntity)
-            }
-        } else {
+        if (_id === 'new' && Object.keys(entity).length) {
+            clearPostOne()
             setPhotos([])
             setTitleSend('')
             setDescription('')
         }
-        return () => {
-            clearState()
-            clearStatus('sentPost')
-        }
     }, [_id]);
 
 
@@ -50,30 +40,40 @@ const EntityEditorPost = ({ match: { params: { _id } }, myID, entity, status, on
     }, [status])
 
     const disabledBtn = photos?.length && titleSend && description ? false : true
-    const sentPost = () => onSave(photos, titleSend, description)
+    const savePost = () => onSave(photos, titleSend, description)
 
     return (
         <Container>
             <ContainEditorPost >
                 <h1 className="title" level={1}>Create / edit Post</h1>
-                <Divider orientation="left" orientationMargin="0"><Title level={3}>Photos</Title></Divider>
+                <Divider orientation="left" orientationMargin="0">
+                    <Title level={3}>Photos</Title>
+                </Divider>
+
                 <EditPhotos photos={photos} setPhotos={setPhotos} />
                 <EditTitlePost titleSend={titleSend} setTitleSend={setTitleSend} />
                 <EditDescriptionPost description={description} setDescription={setDescription} />
-                <Divider orientation="right">   <Button disabled={disabledBtn} type="primary" onClick={sentPost}>Send a Post</Button></Divider>
+
+                <Divider orientation="right">
+                    <Button
+                        disabled={disabledBtn}
+                        type="primary"
+                        onClick={savePost}
+                    >
+                        Save a Post
+                    </Button>
+                </Divider>
             </ContainEditorPost>
         </ Container>
     )
 }
 
-export const CEntityEditorPost = connect(state => ({
-    myID: state?.auth?.payload?.sub?.id,
-    entity: state?.post.posts,
+export const CEditPostPage = connect(state => ({
+    myID: state?.aboutMe?._id,
+    entity: state?.postOne,
     status: state?.promise?.sentPost?.status
 }),
     {
-        updatePost: actionAddPostAC,
-        onSave: actionFullSentPost,
-        clearState: actionRemovePostAC,
-        clearStatus: actionClearPromise
-    })(EntityEditorPost)
+        onSave: actionCreatePostSagaAC,
+        clearPostOne: actionClearPostsOneAC
+    })(EditPostPage)

+ 0 - 129
src/pages/MainPostsFeed.jsx

@@ -1,129 +0,0 @@
-import { Card, Col, Row, Divider } from 'antd'
-import React, { useEffect, useState } from 'react'
-import { connect } from 'react-redux'
-import { Link } from 'react-router-dom'
-import Paragraph from 'antd/lib/typography/Paragraph'
-import Text from 'antd/lib/typography/Text'
-import { actionPostsFeed, actionRemovePostAC } from '../actions'
-import { DateCreated } from '../components/main/DateCreated'
-import PostImage from '../components/main/postsFeed/PostImage'
-import { CPostUserPanel } from '../components/main/postsFeed/PostUserPanel'
-import { Container } from './Content'
-import { CPostTitle } from '../components/main/post/PostTitle'
-import { CPreloader } from './Preloader'
-import { CFieldCommentSend } from '../components/main/postsFeed/FieldComment'
-import { PostCommentDate } from '../components/main/post/PostComment'
-import Title from 'antd/lib/typography/Title'
-
-
-export const PostDescription = ({ title, description, date }) =>
-    <>
-        <Row justify='space-between'>
-            <Col >
-                {!!title && <Text level={3} strong>{title}</Text>}
-            </Col>
-            <Col >
-                <Text type='secondary'>
-                    <DateCreated date={date} />
-                </Text>
-            </Col>
-        </Row>
-        <Paragraph ellipsis={true ? { rows: 1, expandable: true, symbol: '...More' } : false}>
-            {description}
-        </Paragraph>
-    </>
-
-const CommentPostFeed = ({ comment }) =>
-    <div className='CommentPostFeed'>
-        <Link to={`/profile/${comment.owner._id}`}>
-            {comment?.owner?.nick || comment?.owner?.login}
-        </Link>
-        <PostCommentDate createdAt={comment.createdAt} />
-        <Paragraph ellipsis={{ rows: 1, expandable: true, symbol: 'more' }}>
-            {comment?.text}
-        </Paragraph>
-    </div>
-
-export const CommentSPostFeed = ({ comments = [], _id }) =>
-    <>
-        {
-            comments && comments.length
-                ? <>
-                    {(comments.length > 2) && <Link to={`/post/${_id}`}>
-                        <Divider orientation="left">
-                            {comments?.length ? `View ${comments.length} comments` : 'No comments'}
-                        </Divider>
-                    </Link>}
-                    {comments.slice(0, 2).map(c => <CommentPostFeed key={c._id} comment={c} />)}
-                </>
-                : <Link to={`/post/${_id}`}>
-                    <Divider orientation="left">
-                        {comments?.length ? `View ${comments.length} comments` : 'No comments'}
-                    </Divider>
-                </Link>
-        }
-        <CFieldCommentSend id={_id} setOpen={() => { }} />
-    </>
-
-const Post = ({ postData: { _id, text, title, owner, images, createdAt = '', comments, likes, collections } }) =>
-    <div className='Post'>
-        <Card
-            title={<CPostTitle owner={owner} postId={_id} />}
-            cover={<PostImage images={images} />}
-        >
-            <CPostUserPanel postId={_id}
-                likes={likes}
-                collections={collections}
-                styleFontSize='1.7em' />
-            <PostDescription title={title} description={text} date={createdAt} />
-            <CommentSPostFeed comments={comments} _id={_id} />
-        </Card>
-    </div>
-
-
-const MainPostsFeed = ({ posts, postsFollowing, clearState, following }) => {
-
-    const [checkScroll, setCheckScroll] = useState(true)
-
-    useEffect(() => {
-        if (checkScroll && following.length !== 0) {
-            postsFollowing()
-            setCheckScroll(false)
-        }
-    }, [checkScroll, following])
-
-    useEffect(() => {
-        document.addEventListener('scroll', scrollHandler)
-        return () => {
-            document.removeEventListener('scroll', scrollHandler)
-            clearState()
-
-        }
-    }, [])
-
-    const scrollHandler = (e) => {
-        if (e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < 500) {
-            setCheckScroll(true)
-        }
-    }
-
-    return (
-        <Container>
-            <CPreloader promiseName='followingPosts' />
-            {Array.isArray(posts) && posts.length
-                ? posts.map(p => <Post key={p._id} postData={p} />)
-                : <Title level={4}>
-                    The tape is empty. Subscribe to users to see them
-                    posts or create your own
-                </Title>}
-        </Container>
-    )
-}
-
-export const CMainPostsFeed = connect(state => ({
-    posts: state?.post?.posts || [],
-    following: state?.myData?.following || []
-}), {
-    postsFollowing: actionPostsFeed,
-    clearState: actionRemovePostAC,
-})(MainPostsFeed)

+ 57 - 0
src/pages/PostOnePage.jsx

@@ -0,0 +1,57 @@
+import React from 'react'
+import { Divider } from 'antd';
+import { connect } from 'react-redux'
+import Text from 'antd/lib/typography/Text';
+import PostImageCover from '../components/post/PostImageCover';
+import { CEditMyPostBtn, PostHeader } from '../components/post/PostHeader';
+import { CPostDescription } from '../components/post/PostDescription';
+import { CPostOneUserPanel } from '../components/post/PostUserPanel';
+import { CCommentFieldPostOne } from '../components/CommentField';
+import { CPostOneComments } from '../components/post/PostOneComment';
+import { CEditPostPage } from './EditPostPage';
+import { CPreloader } from '../components/Preloader';
+
+
+const PostOnePageDescrption = ({ data: { _id, likes } }) =>
+    <div className='PostOne__description-inner'>
+        <div className='PostOne__description-top'>
+            <CPostDescription />
+            <Divider plain>
+                <Text type='secodary'>Comments</Text>
+            </Divider>
+            <div className='PostOne__comments'>
+                <CPostOneComments />
+            </div>
+        </div>
+        <div className='PostOne__description-bottom'>
+            <Divider />
+            <CPostOneUserPanel likes={likes} postId={_id}
+                styleFontSize='1.3em' />
+            <CCommentFieldPostOne id={_id} setOpen={() => { }} />
+        </div>
+    </div>
+
+
+const CPostOnePageDescrption = connect(state => ({ data: state?.postOne || {} }))(PostOnePageDescrption)
+
+
+const PostOnePage = ({ data: { _id, images, owner } }) =>
+    < div className='PostOne' >
+        <CPreloader promiseName='postOne' />
+        <div className='PostOne__inner' >
+            <div className='PostOne__image'>
+                <PostImageCover images={images} />
+            </div>
+            <div className='PostOne__title'>
+                <PostHeader owner={owner}>
+                    <CEditMyPostBtn owner={owner} postId={_id} />
+                </PostHeader>
+            </div>
+            <div className='PostOne__description'>
+                <CPostOnePageDescrption />
+            </div>
+        </div >
+    </div >
+
+
+export const CPostOnePage = connect(state => ({ data: state?.postOne || {} }))(PostOnePage)

+ 0 - 57
src/pages/PostPage.jsx

@@ -1,57 +0,0 @@
-import React from 'react'
-import { Divider } from 'antd';
-import { connect } from 'react-redux'
-import PostImage from '../components/main/postsFeed/PostImage'
-import Text from 'antd/lib/typography/Text';
-import { CFieldCommentSend } from '../components/main/postsFeed/FieldComment';
-import { CPostUserPanel } from '../components/main/postsFeed/PostUserPanel';
-import { CPostTitle } from '../components/main/post/PostTitle';
-import { CPreloader } from './Preloader';
-import { CPostComments } from '../components/main/post/PostComment';
-import { PostDescription } from './MainPostsFeed';
-
-
-
-const PostPageTitle = ({ data: { owner }, postId }) =>
-    <CPostTitle owner={owner} postId={postId} />
-
-const CPostPageTitle = connect(state => ({ data: state?.post?.posts || {}, postId: state?.post?.posts?._id }))(PostPageTitle)
-
-const PostPageDescrption = ({ data: { _id, likes, text, title, createdAt, } }) =>
-    <div className='PostOne__description-inner'>
-        <div className='PostOne__description-top'>
-            <PostDescription title={title} description={text} date={createdAt} />
-            <Divider plain><Text type='secodary'>Comments</Text></Divider>
-            <div className='PostOne__comments'>
-                <CPostComments />
-            </div>
-        </div>
-        <div className='PostOne__description-bottom'>
-            <Divider />
-            <CPostUserPanel likes={likes} postId={_id}
-                styleFontSize='1.3em' />
-            <CFieldCommentSend id={_id} setOpen={() => { }} /> {/* setOpen - функция заглушка для пропса компонента*/}
-        </div>
-    </div>
-
-
-const CPostPageDescrption = connect(state => ({ data: state?.post?.posts || {} }))(PostPageDescrption)
-
-
-const PostPage = ({ data: { images } }) =>
-    <div className='PostOne'>
-        <CPreloader promiseName='postOne' />
-        <div className='PostOne__inner'>
-            <div className='PostOne__image'>
-                <PostImage images={images} />
-            </div>
-            <div className='PostOne__title'>
-                <CPostPageTitle />
-            </div>
-            <div className='PostOne__description'>
-                <CPostPageDescrption />
-            </div>
-        </div>
-    </div>
-
-export const CPostPage = connect(state => ({ data: state?.post?.posts || {} }))(PostPage)

+ 77 - 0
src/pages/PostsTapeMyFollowing.jsx

@@ -0,0 +1,77 @@
+import { Card } from 'antd'
+import React, { useEffect, useState } from 'react'
+import { connect } from 'react-redux'
+import Title from 'antd/lib/typography/Title'
+import { Container } from '../components/Container'
+import { actionClearPostsTapeAC, actionGetPostsTapeSagaAC } from '../actions/actonsCreators'
+import { PostHeader } from '../components/post/PostHeader'
+import PostImageCover from '../components/post/PostImageCover'
+import { CPostTapeUserPanel } from '../components/post/PostUserPanel'
+import { PostDescription } from '../components/post/PostDescription'
+import { CommentsPostInTape } from '../components/post/CommentsPostInTape'
+import { CPreloader } from '../components/Preloader'
+
+
+const Post = ({ postData: { _id, text, title, owner, images, createdAt = '', comments, likes, collections } }) =>
+    <div className='Post'>
+        <Card
+            title={<PostHeader owner={owner} />}
+            cover={<PostImageCover images={images} />}
+        >
+            <CPostTapeUserPanel
+                postId={_id}
+                likes={likes}
+                collections={collections}
+                styleFontSize='1.7em' />
+            <PostDescription title={title} description={text} date={createdAt} />
+            <CommentsPostInTape comments={comments} _id={_id} />
+        </Card>
+    </div>
+
+
+const PostsTapeMyFollowing = ({ posts, onGetPostsTape, clearPostsTape, following, status }) => {
+
+    const [checkScroll, setCheckScroll] = useState(true)
+
+    useEffect(() => {
+        if (checkScroll && following.length !== 0) {
+            onGetPostsTape()
+            setCheckScroll(false)
+        } 
+    }, [checkScroll, following])
+
+    useEffect(() => {
+        document.addEventListener('scroll', scrollHandler)
+        return () => {
+            document.removeEventListener('scroll', scrollHandler)
+            clearPostsTape()
+        }
+    }, [])
+
+    const scrollHandler = (e) => {
+        if (e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < 500) {
+            setCheckScroll(true)
+        }
+    }
+
+    return (
+        <Container>
+            <CPreloader promiseName='followingPosts' />
+            {posts.length
+                ? posts.map(p => <Post key={p._id} postData={p} />)
+                : <Title level={4}>
+                    The tape is empty. Subscribe to users to see them
+                    posts or create your own
+                </Title>}
+        </Container>
+    )
+}
+
+export const CPostsTapeMyFollowing = connect(state => ({
+    posts: state?.postsTape?.posts || [],
+    following: state?.aboutMe?.following || [],
+    status: state?.promise?.followingPosts
+}), {
+    onGetPostsTape: actionGetPostsTapeSagaAC,
+    clearPostsTape: actionClearPostsTapeAC,
+})(PostsTapeMyFollowing)

+ 119 - 71
src/pages/ProfilePage.jsx

@@ -2,100 +2,147 @@ import React, { useEffect, useState } from 'react'
 import { Button, Col, Row } from 'antd'
 import { connect } from 'react-redux'
 import { Link } from 'react-router-dom'
-import { actionProfilePageData, actionRemovePostAC, actionSubscribe, actionUnSubscribe } from '../actions'
-import { CModalFollowers, CModalFollowing } from '../components/main/profilePage/ModalFollow'
-import { DateCreated } from '../components/main/DateCreated'
+import { actionChangeSubscribeSagaAC, actionClearPostsTapeAC, actionClearProfileDataAC, actionProfileDataSagaAC } from '../actions/actonsCreators'
+import { Container } from '../components/Container'
+import { UserAvatar } from '../components/UserAvatar'
+import { DateCreated } from '../components/post/PostDescription'
 import Text from 'antd/lib/typography/Text'
-import { Container } from './Content'
-import { CPosts } from '../components/main/Posts'
-import { UserAvatar } from '../components/header/UserAvatar'
-import { CPreloader } from './Preloader'
+import { CModalFollowers, CModalFollowing } from '../components/ModalFollow'
+import { CGalleryMediaPostsUser } from '../components/GalleryMediaPostsUser'
+import { CPreloader } from '../components/Preloader'
 
-const ProfileFollowButton = ({ myID, userId, followers, onSubsuscribe, onUnSubsuscribe }) => {
+
+const ProfileFollowButton = ({ myID, userId, followers, onChangeSubscribe }) => {
     const followCheck = followers.find(f => f._id === myID && true)
     return (
         <Col className='Profile__setting'>
             {!!followCheck ?
-                <Button onClick={() => onUnSubsuscribe(userId)}>UnSubscribe</Button> :
-                <Button onClick={() => onSubsuscribe(userId)} type="primary">Subscribe</Button>}
+                <Button onClick={() => onChangeSubscribe(userId, followCheck)}>UnSubscribe</Button> :
+                <Button onClick={() => onChangeSubscribe(userId, followCheck)} type="primary">Subscribe</Button>}
         </Col>
     )
 }
 
-export const CProfileFollowButton = connect(state => ({
-    myID: state?.auth?.payload?.sub.id,
-    followers: state?.post?.userData?.followers || []
-}), { onSubsuscribe: actionSubscribe, onUnSubsuscribe: actionUnSubscribe })(ProfileFollowButton)
+
+const CProfileFollowButton = connect(state => (
+    {
+        myID: state?.aboutMe._id,
+        followers: state?.dataProfile?.followers || []
+    }),
+    {
+        onChangeSubscribe: actionChangeSubscribeSagaAC
+    })(ProfileFollowButton)
+
+
+const ProfilePageName = ({ myID, data: { _id, login, nick } }) =>
+    <Row align='top' justify='space-between' className='Profile__name'>
+        <Col>
+            <h1>{nick || login || 'No Name'}</h1>
+            <span className='Profile__login'>{login || '----'}</span>
+        </Col>
+        <Col >
+            {myID !== _id
+                ? <CProfileFollowButton userId={_id} />
+                : <Button type=''>
+                    <Link to={`/my-settings`}>Settings</Link>
+                </Button>}
+        </Col>
+    </Row>
+
+const CProfilePageName = connect(state => ({
+    myID: state?.aboutMe?._id,
+    data: state?.dataProfile
+}))(ProfilePageName)
+
+
+const ProfilePageCreateAt = ({ myID, data: { _id, createdAt } }) =>
+    <Row align='middle' justify='space-between'>
+        <Col className='Profile__created'>
+            <Text type='secondary'>
+                Account created: <DateCreated date={createdAt} />
+            </Text>
+        </Col>
+        {myID !== _id &&
+            <Col offset={1}>
+                <Link className='Profile__link-message' to='/message'>
+                    Send message
+                </Link>
+            </Col>
+        }
+    </Row>
+
+const CProfilePageCreateAt = connect(state => ({
+    myID: state?.aboutMe?._id,
+    data: state?.dataProfile
+}))(ProfilePageCreateAt)
 
 
-const ProfilePageData = ({ myID, data: { _id, avatar, login, nick, createdAt = '', followers, following }, count, setFollowing, setFollowers }) =>
+const ProfilePageStatistic = ({ count, data: { followers, following } }) => {
+    const [openFollowers, setOpenFollowers] = useState(false)
+    const [openFollowing, setOpenFollowing] = useState(false)
+    return (
+        <Row className='Profile__count' align='middle' justify='space-between' >
+            <Col >
+                <strong>{count || '0'}</strong>
+                <span>Posts</span>
+            </Col>
+            <Col >
+                <Button type="link" onClick={() => setOpenFollowers(true)}>
+                    <strong>{followers?.length || '0'}</strong>
+                    <span>Followers</span>
+                </Button>
+            </Col>
+            <Col >
+                <Button type="link" onClick={() => setOpenFollowing(true)}>
+                    <strong>{following?.length || '0'}</strong>
+                    <span>Following</span>
+                </Button>
+            </Col>
+            {openFollowers && < CModalFollowers statusModal={setOpenFollowers} title={'Followers'} />}
+            {openFollowing && < CModalFollowing statusModal={setOpenFollowing} title={'Following'} />}
+        </ Row>
+    )
+}
+
+const CProfilePageStatistic = connect(state => ({
+    data: state?.dataProfile,
+    count: state?.postsTape?.count
+}))(ProfilePageStatistic)
+
+
+const ProfilePageData = ({ children }) =>
+    <>{children} </>
+
+
+const ProfilePageDataContainer = ({ avatar }) =>
     <Row className='Profile' >
         <Col flex={'150px'}>
             <UserAvatar avatarSize={'150px'} avatar={avatar} />
         </Col>
         <Col className='Profile__data' flex={'auto'} offset={2}>
-            <Row align='top' justify='space-between' className='Profile__name'>
-                <Col>
-                    <h1>{nick || login || 'No Name'}</h1>
-                    <span className='Profile__login'>{login || '----'}</span>
-                </Col>
-                <Col >
-                    {myID !== _id
-                        ? <CProfileFollowButton userId={_id} />
-                        : <Button type=''><Link to={`/my-settings`}>Settings</Link></Button>}
-                </Col>
-            </Row>
-            <Row align='middle' justify='space-between'>
-                <Col className='Profile__created'>
-                    <Text type='secondary'>Account created: <DateCreated date={createdAt} /></Text>
-                </Col>
-                {myID !== _id && <Col offset={1}>
-                    <Link className='Profile__link-message' to='/message'>Send message</Link>
-                </Col>}
-            </Row>
-            <Row className='Profile__count' align='middle' justify='space-between'>
-                <Col >
-                    <strong>{count || '0'}</strong>
-                    <span>Posts</span>
-                </Col>
-                <Col >
-                    <Button type="link" onClick={() => setFollowers(true)}>
-                        <strong>{followers?.length || '0'}</strong>
-                        <span>Followers</span>
-                    </Button>
-                </Col>
-                <Col >
-                    <Button type="link" onClick={() => setFollowing(true)}>
-                        <strong>{following?.length || '0'}</strong>
-                        <span>Following</span>
-                    </Button>
-                </Col>
-            </Row>
-        </Col >
+            <ProfilePageData>
+                <CProfilePageName />
+                <CProfilePageCreateAt />
+                <CProfilePageStatistic />
+            </ProfilePageData>
+        </Col>
     </Row >
 
+const CProfilePageData = connect(state => ({ avatar: state?.dataProfile?.avatar }))(ProfilePageDataContainer)
 
-const CProfilePageData = connect(state => ({
-    myID: state.auth.payload.sub.id || '',
-    data: state?.post?.userData || {},
-    count: state?.post?.count || null
-}))(ProfilePageData)
 
 
+const ProfilePage = ({ match: { params: { _id } }, getProfileUser, clearProfileData, clearPostsTape }) => {
 
-
-const ProfilePage = ({ match: { params: { _id } }, getProfileUser, clearDataProfile }) => {
-    const [followers, setFollowers] = useState(false)
-    const [following, setFollowing] = useState(false)
     const [checkScroll, setCheckScroll] = useState(true)
 
-
     useEffect(() => {
         document.addEventListener('scroll', scrollHandler)
         return () => {
             document.removeEventListener('scroll', scrollHandler)
             setCheckScroll(true)
-            clearDataProfile()
+            clearProfileData()
+            clearPostsTape()
         }
     }, [_id])
 
@@ -115,14 +162,15 @@ const ProfilePage = ({ match: { params: { _id } }, getProfileUser, clearDataProf
     return (
         <Container>
             <CPreloader promiseName='userOneDataPosts'/>
-            <CProfilePageData setFollowing={setFollowing} setFollowers={setFollowers} />
-            {followers && < CModalFollowers statusModal={setFollowers} title={'Followers'} />}
-            {following && < CModalFollowing statusModal={setFollowing} title={'Following'} />}
-            <CPosts />
+            <CProfilePageData />
+            <CGalleryMediaPostsUser />
         </Container>
     )
 }
 
-export const CProfilePage = connect(state => ({
-    posts: state?.post?.posts || []
-}), { getProfileUser: actionProfilePageData, clearDataProfile: actionRemovePostAC })(ProfilePage)
+export const CProfilePage = connect(null,
+    {
+        getProfileUser: actionProfileDataSagaAC,
+        clearProfileData: actionClearProfileDataAC,
+        clearPostsTape: actionClearPostsTapeAC,
+    })(ProfilePage)

+ 48 - 23
src/pages/SettingsPage.jsx

@@ -1,12 +1,13 @@
 import React, { useEffect, useState } from 'react';
-import { Container } from './Content';
-import { CEditAvatar } from '../components/main/profilePage/EditAvatar'
 import { Button, Col, Divider, Input, message, Row, Space } from 'antd'
 import Title from 'antd/lib/typography/Title';
-import { connect } from 'react-redux';
-import { EditOutlined, LogoutOutlined } from '@ant-design/icons';
 import Text from 'antd/lib/typography/Text';
-import { actionAuthLogout, actionFullAboutMeUpsert, actionRemoveMyDataAC } from '../actions';
+import { EditOutlined, LogoutOutlined } from '@ant-design/icons';
+import { connect } from 'react-redux';
+import { Container } from '../components/Container';
+import { actionAboutMeUpsertSagaAC, actionAuthLogoutAC, actionClearAboutMeDataAC } from '../actions/actonsCreators';
+import { CChangeAvatar } from '../components/ChangeAvatar';
+
 
 
 const ContainerSettingsPage = ({ children }) =>
@@ -17,13 +18,13 @@ const EditMyDataIput = ({ title, propValue, propHandler, error, setError, setChe
     const [value, setValue] = useState(propValue);
     const [editMode, setEditMode] = useState(false);
 
-
     useEffect(() => {
         setValue(propValue)
     }, [propValue]);
 
     const addValueHandler = () => {
         const valid = /^[A-Z][a-z0-9_]{1,15}$/
+
         if (valid.test(value)) {
             propHandler(value)
             setEditMode(false)
@@ -42,14 +43,19 @@ const EditMyDataIput = ({ title, propValue, propHandler, error, setError, setChe
         <label onDoubleClick={() => setEditMode(true)}>
             <Title level={4}>{title} :</Title>
             <div className='EditMyData__lable-box'>
-                {error && <Text type='danger'> First letter is capitalized!!!</Text>}
+                {error &&
+                    <Text type='danger'>
+                        No spaces,First letter is capitalized!!!
+                    </Text>}
                 {!editMode
-                    ? <Text className='EditMyData__lable-text'>{value}
+                    ?
+                    <Text className='EditMyData__lable-text'>{value}
                         <EditOutlined
                             onClick={() => setEditMode(true)}
                             style={{ fontSize: '1.1em', color: '#1890ff ' }} />
                     </Text>
-                    : <Input className={error && '--error'} value={value}
+                    :
+                    <Input className={error && '--error'} value={value}
                         onBlur={addValueHandler}
                         onChange={onChangeInput}
                         onPressEnter={addValueHandler}
@@ -105,31 +111,50 @@ const EditMyData = ({ myData, status, onUpsert }) => {
     )
 }
 
-const CEditMyData = connect(state => ({ myData: state?.myData, status: state?.promise?.upsertAboutMe?.status }), { onUpsert: actionFullAboutMeUpsert })(EditMyData)
+const CEditMyData = connect(state => ({
+    myData: state?.aboutMe,
+    status: state?.promise?.upsertAboutMe?.status
+}), {
+    onUpsert: actionAboutMeUpsertSagaAC
+})(EditMyData)
+
+
+const SettingsPageInner = () =>
+    <Row >
+        <Col flex={1}>
+            <CChangeAvatar />
+        </Col>
+        <Col flex={4} offset={1} className='EditMyData'>
+            <CEditMyData />
+        </Col>
+    </Row>
+
 
-const SettingsPage = ({ onLogOut, removeMydata }) => {
+const SettingsPage = ({ onLogOut, clearMydata }) => {
     const handlerExitBtn = () => {
         onLogOut()
-        removeMydata()
+        clearMydata()
     }
     return (
         <Container>
             <ContainerSettingsPage>
-                <Divider><Title level={2}>Profile Settings</Title></Divider>
-                <Row >
-                    <Col flex={1}>
-                        <CEditAvatar />
-                    </Col>
-                    <Col flex={4} offset={1} className='EditMyData'>
-                        <CEditMyData />
-                    </Col>
-                </Row>
+                <Divider>
+                    <Title level={2}>
+                        Profile Settings
+                    </Title>
+                </Divider>
+                <SettingsPageInner />
                 <Space className='Exit-box__btn'>
-                    <Button onClick={handlerExitBtn}><LogoutOutlined /> Exit</Button>
+                    <Button onClick={handlerExitBtn}><LogoutOutlined />
+                        Exit
+                    </Button>
                 </Space>
             </ContainerSettingsPage>
         </Container>
     )
 }
 
-export const CSettingsPage = connect(null, { onLogOut: actionAuthLogout, removeMydata: actionRemoveMyDataAC })(SettingsPage)
+export const CSettingsPage = connect(null, {
+    onLogOut: actionAuthLogoutAC,
+    clearMydata: actionClearAboutMeDataAC
+})(SettingsPage)

+ 18 - 0
src/redux/reducers/aboutMe-reducer.js

@@ -0,0 +1,18 @@
+
+export const aboutMeReducer = (state = {}, { type, data }) => {
+    const types = {
+        'ABOUTME-DATA-ADD': () => ({ ...state, ...data }),
+
+        'CHANGE-ABOUTME-AVATAR': () =>  ({ ...state, avatar: { ...data } }),
+
+        'UPDATE-MY-FOLLOWING': () => ({ ...state, following: [...data] }),
+
+        'UPSERT-COLLECTION': () => ({...state, collection:data }),
+        
+        'CLEAR-ABOUTME': () => ({})
+    }
+    if (type in types) {
+        return types[type]()
+    }
+    return state
+}

+ 10 - 1
src/redux/reducers/auth-reducer.js

@@ -1,5 +1,14 @@
-import { jwtDecode } from '../../helpers'
 
+const jwtDecode = (token) => {
+    try {
+        let arrToken = token.split('.')
+        let base64Token = atob(arrToken[1])
+        return JSON.parse(base64Token)
+    }
+    catch (e) {
+        console.log('Ой, ошибочка вышла ' + e);
+    }
+}
 
 export const authReducer = (state, { type, token, remember }) => {
     if (!state) {

+ 0 - 22
src/redux/reducers/myProfile-reducer.js

@@ -1,22 +0,0 @@
-
-export const myProfileReducer = (state = {}, { type, data }) => {
-    const types = {
-        'ABOUTME-DATA-ADD': () => {
-            return { ...state, ...data }
-        },
-        'ABOUTME-UPDATE-AVATAR': () => {
-            return { ...state, avatar: { ...data } }
-        },
-        'UPDATE-MY-FOLLOWING': () => {
-            return { ...state, following: [...data] }
-        },
-        'UPSERT-COLLECTION': () => ({
-            ...state, collections: data
-        }),
-        'REMOVE-MYDATA': () => ({})
-    }
-    if (type in types) {
-        return types[type]()
-    }
-    return state
-}

+ 0 - 96
src/redux/reducers/post-reducer.js

@@ -1,96 +0,0 @@
-export const postReducer = (state = {}, { type, findId, newResult, userData = {}, count = null }) => {
-
-    const { posts } = state
-
-    const upsertSubComments = (commentList, id, nR, find) => {
-        return commentList.map(c => {
-            if (c._id === id) {
-                return { ...c, [find]:  nR }
-            } else if (c?.answers?.length) {
-                return {
-                    ...c,
-                    answers: upsertSubComments(c.answers, id, nR, find)
-                }
-            } else {
-                return { ...c }
-            }
-        })
-    }
-
-    const types = {
-
-        'ADD-POSTS': () => ({
-            ...state,
-            posts: Array.isArray(newResult)
-                ? [...posts, ...newResult]
-                : { ...posts, ...newResult },
-            count
-        }),
-
-        'ADD-PROFILE-DATA': () => ({
-            ...state,
-            posts: !!posts ? [...posts, ...newResult] : [...newResult],
-            userData,
-            count
-        }),
-
-        'REMOVE-POSTS': () => ({
-            ...state,
-            posts: [],
-            userData: {},
-            count: 0,
-            subComments: {},
-        }),
-
-        'ADD-POST-LIKE': () => ({
-            ...state,
-            posts: Array.isArray(posts)
-                ? posts.map(p => p._id === findId ? p = { ...p, likes: [...newResult] } : p)
-                : { ...state.posts, likes: [...newResult] },
-        }),
-
-        'REMOVE-POST-LIKE': () => ({
-            ...state,
-            posts: Array.isArray(posts)
-                ? posts.map(p => p._id === findId ? p = { ...p, likes: [...newResult] } : p)
-                : { ...state.posts, likes: [...newResult] },
-        }),
-
-        'ADD-COMMENT': () => ({
-            ...state,
-            posts: Array.isArray(posts)
-                ? posts.map(p => p._id === findId ? p = { ...p, comments: [...newResult] } : p)
-                : { ...state.posts, comments: [...newResult] }
-        }),
-
-        'UPDATE-SUBCOMMENT': () => ({
-            ...state, posts: { ...state.posts, comments: upsertSubComments(posts.comments, findId, newResult, 'answers') }
-        }),
-
-        'EDIT-COMMENT': () => ({
-            ...state, posts: { ...state.posts, comments: upsertSubComments(posts.comments, findId, newResult.text, 'text') }
-        }),
-
-        'UPSERT-LIKE-COMMENT': () => ({
-            ...state, posts: {
-                ...state.posts, comments: upsertSubComments(posts.comments, findId, newResult, 'likes')
-            }
-        }),
-
-        'UPDATE-FOLLOWERS': () => ({
-            ...state,
-            userData: { ...state.userData, followers: [...newResult] }
-        }),
-    }
-    
-    if (type in types) {
-        return types[type]()
-    }
-
-    return state
-}
-
-
-
-
-

+ 53 - 0
src/redux/reducers/postOne-reducer.js

@@ -0,0 +1,53 @@
+export const postOneReducer = (state = {}, { type, commentId, newResult, userData = {}, count = null }) => {
+
+    const changeComments = (commentList, id, nR, find) =>
+        commentList.map(c => {
+            if (c._id === id) {
+                return { ...c, [find]: nR }
+            } else if (c?.answers?.length) {
+                return {
+                    ...c,
+                    answers: changeComments(c.answers, id, nR, find)
+                }
+            } else {
+                return { ...c }
+            }
+        })
+
+
+    const types = {
+
+        'POST-ONE-DATA': () => ({ ...newResult }),
+
+        'CLEAR-POST-ONE': () => ({}),
+
+        'POST-ONE-LIKE': () => ({ ...state, likes: [...newResult] }),
+
+        'POST-ONE-ADD-COMMENT': () => ({ ...state, comments: [...newResult] }),
+
+        'UPDATE-COMMENT': () => ({
+            ...state,
+            comments: changeComments(state.comments, commentId, newResult, 'answers')
+        }),
+
+        'EDIT-COMMENT': () => ({
+            ...state,
+            comments: changeComments(state.comments, commentId, newResult.text, 'text')
+        }),
+
+        'LIKE-COMMENT': () => ({
+            ...state, comments: changeComments(state.comments, commentId, newResult, 'likes')
+        }),
+    }
+
+    if (type in types) {
+        return types[type]()
+    }
+
+    return state
+}
+
+
+
+
+

+ 40 - 0
src/redux/reducers/postsTape-reducer.js

@@ -0,0 +1,40 @@
+export const postsTapeReducer = (state = {}, { type, newResult, count, postId }) => {
+
+    const { posts } = state
+
+    const types = {
+
+        'POSTS-TAPE': () => ({
+            ...state,
+            posts: [...posts || [], ...newResult],
+            count: count ? count : state.count
+        }),
+
+        'CLEAR-POSTS-TAPE': () => ({
+            ...state,
+            posts: [],
+            count: 0,
+        }),
+
+        'POSTS-TAPE-LIKE': () => ({
+            ...state,
+            posts: posts.map(p => p._id === postId ? p = { ...p, likes: [...newResult] } : p)
+        }),
+
+        'ADD-COMMENT-POST-TAPE': () => ({
+            ...state,
+            posts: posts.map(p => p._id === postId ? p = { ...p, comments: [...newResult] } : p)
+        }),
+    }
+
+    if (type in types) {
+        return types[type]()
+    }
+
+    return state
+}
+
+
+
+
+

+ 14 - 0
src/redux/reducers/profileData-reducer.js

@@ -0,0 +1,14 @@
+
+export const profileDataReducer = (state = {}, { type, data }) => {
+    const types = {
+        'PROFILE-DATA': () => ({ ...state, ...data }),
+
+        'UPDATE-USER-FOLLOWERS': () => ({ ...state, followers: [...data] }),
+
+        'CLEAR-PROFILE-DATA': () => ({})
+    }
+    if (type in types) {
+        return types[type]()
+    }
+    return state
+}

+ 0 - 5
src/redux/reducers/route-reducer.js

@@ -1,5 +0,0 @@
-export const routeReducer = (state = {}, { type, match }) => {
-    if (type === 'ROUTE')
-        return match
-    return state
-}

+ 13 - 9
src/redux/redux-store.js

@@ -1,26 +1,30 @@
 import { createStore, combineReducers, applyMiddleware } from 'redux';
 import { authReducer } from './reducers/auth-reducer';
-import { myProfileReducer } from './reducers/myProfile-reducer';
-import { postReducer } from './reducers/post-reducer';
-import { promiseReducer } from './reducers/promise-reducer';
+// import { postReducer } from './reducers/post-reducer';
 import createSagaMiddleware from 'redux-saga'
+import { promiseReducer } from './reducers/promise-reducer';
 import { rootSaga } from './saga';
-import { actionFullAboutMe } from '../actions'
-import { routeReducer } from './reducers/route-reducer';
+import { actionAboutMeSagaAC } from '../actions/actonsCreators';
+import { aboutMeReducer } from './reducers/aboutMe-reducer';
+import { postsTapeReducer } from './reducers/postsTape-reducer';
+import { profileDataReducer } from './reducers/profileData-reducer';
+import { postOneReducer } from './reducers/postOne-reducer';
+
 
 const sagaMiddleware = createSagaMiddleware()
 
 const store = createStore(combineReducers({
     auth: authReducer,
     promise: promiseReducer,
-    myData: myProfileReducer,
-    post: postReducer,
-    route: routeReducer,
+    aboutMe: aboutMeReducer,
+    postsTape: postsTapeReducer,
+    postOne: postOneReducer,
+    dataProfile: profileDataReducer,
 }),
     applyMiddleware(sagaMiddleware))
 
 sagaMiddleware.run(rootSaga)
 
-store.dispatch(actionFullAboutMe())
+store.dispatch(actionAboutMeSagaAC())
 
 export default store;

Plik diff jest za duży
+ 143 - 158
src/redux/saga/index.js