Browse Source

Set up posts feed, add like a post and delete like

LenDoc 2 years ago
parent
commit
46f281bea2
9 changed files with 929 additions and 457 deletions
  1. 99 12
      package-lock.json
  2. 7 1
      package.json
  3. 157 142
      src/App.js
  4. 25 0
      src/App.scss
  5. 166 58
      src/actions/index.js
  6. 87 102
      src/components/Post.js
  7. 72 0
      src/components/PostFeed.js
  8. 208 0
      src/components/Post_Comment.js
  9. 108 142
      src/components/User.js

+ 99 - 12
package-lock.json

@@ -16,14 +16,20 @@
         "antd-img-crop": "^4.1.0",
         "array-move": "^4.0.0",
         "cors": "^2.8.5",
+        "emoji-mart": "^3.0.1",
+        "emoji-picker-react": "^3.5.1",
+        "emoji-regex": "^10.1.0",
+        "moment": "^2.29.2",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "react-dropzone": "^12.0.2",
+        "react-input-emoji": "^4.1.0",
         "react-redux": "^7.2.6",
         "react-responsive-carousel": "^3.2.22",
         "react-router-dom": "^5.3.0",
         "react-scripts": "5.0.0",
         "react-sortable-hoc": "^2.0.0",
+        "react-string-replace": "^1.0.0",
         "redux": "^4.1.2",
         "redux-thunk": "^2.4.1",
         "web-vitals": "^2.1.4"
@@ -6459,10 +6465,27 @@
         "url": "https://github.com/sindresorhus/emittery?sponsor=1"
       }
     },
+    "node_modules/emoji-mart": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.1.tgz",
+      "integrity": "sha512-sxpmMKxqLvcscu6mFn9ITHeZNkGzIvD0BSNFE/LJESPbCA8s1jM6bCDPjWbV31xHq7JXaxgpHxLB54RCbBZSlg==",
+      "dependencies": {
+        "@babel/runtime": "^7.0.0",
+        "prop-types": "^15.6.0"
+      },
+      "peerDependencies": {
+        "react": "^0.14.0 || ^15.0.0-0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/emoji-picker-react": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/emoji-picker-react/-/emoji-picker-react-3.5.1.tgz",
+      "integrity": "sha512-cuSthFAvUO8Q1N7KJPEgZ7N0JNIWPKgN5GUmKe71UlSD4qFYI/p05RT9nohlWOq6YSFhEy2mI/60zBZiaku4mg=="
+    },
     "node_modules/emoji-regex": {
-      "version": "9.2.2",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
-      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.1.0.tgz",
+      "integrity": "sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg=="
     },
     "node_modules/emojis-list": {
       "version": "3.0.0",
@@ -6959,6 +6982,11 @@
         "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
       }
     },
+    "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+    },
     "node_modules/eslint-plugin-react": {
       "version": "7.28.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz",
@@ -11303,9 +11331,9 @@
       }
     },
     "node_modules/moment": {
-      "version": "2.29.1",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
-      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
+      "version": "2.29.2",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
+      "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==",
       "engines": {
         "node": "*"
       }
@@ -14038,6 +14066,23 @@
       "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
       "integrity": "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA=="
     },
+    "node_modules/react-input-emoji": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/react-input-emoji/-/react-input-emoji-4.1.0.tgz",
+      "integrity": "sha512-iGOEVTUIhu9d9+hswTElqS1t4onjaa88R7on2a2mDHgFdJKIe62EPiAUEAVA9xqLfeSvb4cPjlbGJ4+ydT12rg==",
+      "dependencies": {
+        "emoji-mart": "3.0.1"
+      },
+      "engines": {
+        "node": ">=8",
+        "npm": ">=5"
+      },
+      "peerDependencies": {
+        "prop-types": ">=15.7.2",
+        "react": ">=17.0.x",
+        "react-dom": ">=17.0.x"
+      }
+    },
     "node_modules/react-is": {
       "version": "17.0.2",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -14227,6 +14272,14 @@
         "react-dom": "^16.3.0 || ^17.0.0"
       }
     },
+    "node_modules/react-string-replace": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.0.0.tgz",
+      "integrity": "sha512-+iLsyE4AeSmnfctgswXOf1PmKRgns6wJ4LVb+8ADMU6mDK3jvBE11QzfMQf7CYtPUUiBCDjZ9ZppzXOIYrzCRg==",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
     "node_modules/readable-stream": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -21742,10 +21795,24 @@
       "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
       "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg=="
     },
+    "emoji-mart": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.1.tgz",
+      "integrity": "sha512-sxpmMKxqLvcscu6mFn9ITHeZNkGzIvD0BSNFE/LJESPbCA8s1jM6bCDPjWbV31xHq7JXaxgpHxLB54RCbBZSlg==",
+      "requires": {
+        "@babel/runtime": "^7.0.0",
+        "prop-types": "^15.6.0"
+      }
+    },
+    "emoji-picker-react": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/emoji-picker-react/-/emoji-picker-react-3.5.1.tgz",
+      "integrity": "sha512-cuSthFAvUO8Q1N7KJPEgZ7N0JNIWPKgN5GUmKe71UlSD4qFYI/p05RT9nohlWOq6YSFhEy2mI/60zBZiaku4mg=="
+    },
     "emoji-regex": {
-      "version": "9.2.2",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
-      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.1.0.tgz",
+      "integrity": "sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg=="
     },
     "emojis-list": {
       "version": "3.0.0",
@@ -22196,6 +22263,13 @@
         "jsx-ast-utils": "^3.2.1",
         "language-tags": "^1.0.5",
         "minimatch": "^3.0.4"
+      },
+      "dependencies": {
+        "emoji-regex": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+          "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+        }
       }
     },
     "eslint-plugin-react": {
@@ -25242,9 +25316,9 @@
       }
     },
     "moment": {
-      "version": "2.29.1",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
-      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+      "version": "2.29.2",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
+      "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg=="
     },
     "ms": {
       "version": "2.1.2",
@@ -27079,6 +27153,14 @@
       "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
       "integrity": "sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA=="
     },
+    "react-input-emoji": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/react-input-emoji/-/react-input-emoji-4.1.0.tgz",
+      "integrity": "sha512-iGOEVTUIhu9d9+hswTElqS1t4onjaa88R7on2a2mDHgFdJKIe62EPiAUEAVA9xqLfeSvb4cPjlbGJ4+ydT12rg==",
+      "requires": {
+        "emoji-mart": "3.0.1"
+      }
+    },
     "react-is": {
       "version": "17.0.2",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -27228,6 +27310,11 @@
         "prop-types": "^15.5.7"
       }
     },
+    "react-string-replace": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.0.0.tgz",
+      "integrity": "sha512-+iLsyE4AeSmnfctgswXOf1PmKRgns6wJ4LVb+8ADMU6mDK3jvBE11QzfMQf7CYtPUUiBCDjZ9ZppzXOIYrzCRg=="
+    },
     "readable-stream": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",

+ 7 - 1
package.json

@@ -2,7 +2,7 @@
   "name": "hipstagram",
   "version": "0.1.0",
   "private": true,
-  "proxy": "http://localhost:5000",
+  "proxy": "http://hipstagram.node.ed.asmer.org.ua",
   "dependencies": {
     "@ant-design/icons": "^4.7.0",
     "@testing-library/jest-dom": "^5.16.2",
@@ -12,14 +12,20 @@
     "antd-img-crop": "^4.1.0",
     "array-move": "^4.0.0",
     "cors": "^2.8.5",
+    "emoji-mart": "^3.0.1",
+    "emoji-picker-react": "^3.5.1",
+    "emoji-regex": "^10.1.0",
+    "moment": "^2.29.2",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "react-dropzone": "^12.0.2",
+    "react-input-emoji": "^4.1.0",
     "react-redux": "^7.2.6",
     "react-responsive-carousel": "^3.2.22",
     "react-router-dom": "^5.3.0",
     "react-scripts": "5.0.0",
     "react-sortable-hoc": "^2.0.0",
+    "react-string-replace": "^1.0.0",
     "redux": "^4.1.2",
     "redux-thunk": "^2.4.1",
     "web-vitals": "^2.1.4"

+ 157 - 142
src/App.js

@@ -5,27 +5,28 @@ import createHistory from 'history/createBrowserHistory'
 import React, { useMemo, useState, useEffect } from 'react'
 import { store } from './reducers'
 import { Basic } from './components/DropZone'
-import {CPageAboutUser,PageAboutUser} from './components/User'
-import { PageCreatePost,AddPost } from './components/NewPost'
-import {CPost,MyCarousel } from './components/Post'
+import { CPageAboutUser, PageAboutUser } from './components/User'
+import { PageCreatePost, AddPost } from './components/NewPost'
+import { CPost, MyCarousel } from './components/Post'
 
 import { sortableContainer, sortableElement } from 'react-sortable-hoc'
 import { arrayMove, arrayMoveImmutable, arrayMoveMutable } from 'array-move'
 import 'antd/dist/antd.css'
 
 import {
-  // backendURL,
   actionAboutMe,
+  actionAllPostsFeed,
+  actionAllPosts,
   actionSetAvatar,
   actionPostsFeed,
   actionAllFollowing,
   actionAllFollowers,
   actionPostsMyFollowing2,
-  actionSearchUser
+  actionSearchUser,
 } from './actions'
 import { Upload, Button, DatePicker, Space } from 'antd'
 import moment from 'moment'
-import { UploadOutlined,SearchOutlined } from '@ant-design/icons'
+import { UploadOutlined, SearchOutlined } from '@ant-design/icons'
 import ImgCrop from 'antd-img-crop'
 import { Avatar, Image, Divider, Radio } from 'antd'
 import { UserOutlined } from '@ant-design/icons'
@@ -33,68 +34,20 @@ import user from './materials/user.png'
 import photoNotFound from './materials/photoNotFound.png'
 // import "react-responsive-carousel/lib/styles/carousel.min.css"; // requires a loader
 // import { Carousel } from 'react-responsive-carousel';
-import { Carousel,Popover } from 'antd'
+import { Carousel, Popover } from 'antd'
 import { LeftCircleFilled, RightCircleFilled } from '@ant-design/icons'
-import { Input,Select } from 'antd';
-import {PagePost} from './components/Post'
+import { Input, Select } from 'antd'
+import { PagePost } from './components/Post'
+import { CPostForFeed,Feed } from './components/PostFeed'
+
 console.log(store.getState())
 store.subscribe(() => console.log(store.getState()))
-// 
-console.log('ABOUT ME',store.getState().auth?.payload?.sub?.id);
+console.log('ABOUT ME', store.getState().auth?.payload?.sub?.id)
 // console.log('ABOUT FOLLOWING',store.getState().promise.aboutUser?.payload?.following);
 //store.dispatch(actionPostsFeed())
 
 const PageMain = () => <div className="PageMain">ГЛАВНАЯ</div>
-const MyPostFeed =({postsFeed, onPostsFeed})=>{
-  useEffect(() => {
-    onPostsFeed()
-    
-  }, [])
- 
- return <> 
-  <h2>Feed</h2>
-
-  <div>
-  {  
-// backendURL +
-    (postsFeed||[]).map(({images, title,text,owner})=>(
-     
-     <div style={{borderWidth:'10',
-     borderColor: '#000',
-     borderStyle: 'solid',
-     marginBottom: '40px', }}>
-
-      {owner?.avatar ? (
-        <Avatar
-          style={{ width: '50px', height: '50px' }}
-          src={ '/' + owner?.avatar?.url}
-        />
-      ) : (
-        <Avatar style={{ width: '50px', height: '50px' }} src={user} />
-      )}
-       <h1> {owner?.login || 'anon'}</h1>
-     
-      <MyCarousel images={images} style={{  marginTop: '60px'}} />
-     <h1> Title: {title || ''}</h1>
-     <h1> Text: {text || ''}</h1>
 
-    </div>
-      
-     )
-    )
-   
-  }
-   </div>
-           
-          {/* </div> */}
- 
- {/* <PagePost onePost={postsFeed}/> <MyCarousel images={postsFeed?.images} />*/}
- 
-  </>
-}
-export const CPostForFeed = connect((state) => ({
-  postsFeed: state.promise?.postsFeed?.payload,
-}), {onPostsFeed:actionPostsFeed})(MyPostFeed)
 
 // const PageFeed = ({ aboutMe,allFollowing, onPostsFeed, onAllFollowing}) => {
 //   console.log('ABOUT FOLLOWING',
@@ -111,7 +64,7 @@ export const CPostForFeed = connect((state) => ({
 // }
 // const CPageFeed =connect((state)=>(
 //   {
-//     aboutMe: state.promise.aboutMe?.payload , 
+//     aboutMe: state.promise.aboutMe?.payload ,
 //   //  allPosts: state.promise?.allPosts?.payload,
 //    postsFeed: state.promise?.postsFeed?.payload,
 //   }), {onPostsFeed:actionPostsFeed})(PageFeed)
@@ -124,131 +77,193 @@ const Main = () => (
       <Route path="/post/:_id" component={CPost} />
       <Route path="/feed" component={CPostForFeed} />
       <Route path="/editProfile" component={CUserEdit} />
+      <Route path="/followers" component={CUserFollowers} />
 
       {/* <CBasic /> */}
     </Switch>
   </main>
 )
+const UserFollowers = ({ followers }) => {
+  console.log('followers ', followers)
+  return (
+    <div>
+      {/* {console.log('start')} */}
+      {(followers || [])?.map(({ _id, login, avatar }) => (
+        <Link to={`/profile/${_id}`}>
+          {console.log('doing something')}
+          <Avatar
+            style={{
+              width: '60px',
+              height: '60px',
+              marginRight: '30px',
+              position: 'absolute',
+            }}
+            src={'/' + avatar?.url || user}
+          />
+          {console.log('followers', login)}
 
-const ResultUserFind =({userFind})=>
-<div>
-{userFind?.map(({_id, login, avatar})=>(
-  <Link to={`/profile/${_id}`} >
-    <Avatar
-          style={{ width: '20px', height: '20px',marginRight:'30px', position: 'absolute' }}
-          src={ '/' + avatar?.url || user}
+          <h1 style={{ marginLeft: '30px' }}> {login}</h1>
+        </Link>
+      ))}
+    </div>
+  )
+}
+
+{
+  /* { console.log('end') } */
+}
+
+// <div>
+//   <ResultUserFind userFind={followers} />
+//   {console.log('followers', followers)}
+// </div>
+const CUserFollowers = connect((state) => ({
+  followers: state.promise.aboutMe?.payload?.followers,
+}))(UserFollowers)
+
+const ResultUserFind = ({ userFind }) => (
+  <div>
+    {userFind?.map(({ _id, login, avatar }) => (
+      <Link to={`/profile/${_id}`}>
+        <Avatar
+          style={{
+            width: '20px',
+            height: '20px',
+            marginRight: '30px',
+            position: 'absolute',
+          }}
+          src={'/' + avatar?.url || user}
         />
-       
-          <h3 style={{ marginLeft:'30px'}} > {login}</h3>
-  </Link>
-))}
-</div>
-// backendURL
 
-const SearchUser =({onSearch,searchUser})=>{
+        <h3 style={{ marginLeft: '30px' }}> {login}</h3>
+      </Link>
+    ))}
+  </div>
+)
+const SearchUser = ({ onSearch, searchUser }) => {
   // const [value, setValue]=useState('')
-  const onSearchUser = value => onSearch(value)
-   const { Search } = Input;
-return <>
-    <Popover  placement="bottom" content={<ResultUserFind userFind={searchUser}/>} trigger="click">
-    <Search
-      placeholder="input search text"
-      allowClear
-      enterButton="Search"
-      size="large"
-      onSearch={onSearchUser}
-    />
-</Popover>
-</>
+  const onSearchUser = (value) => onSearch(value)
+  const { Search } = Input
+  return (
+    <>
+      <Popover
+        placement="bottom"
+        content={<ResultUserFind userFind={searchUser} />}
+        trigger="click"
+      >
+        <Search
+          placeholder="input search text"
+          allowClear
+          enterButton="Search"
+          size="large"
+          onSearch={onSearchUser}
+        />
+      </Popover>
+    </>
+  )
 }
-const CSearch = connect((state)=>({searchUser:state.promise?.searchUser?.payload}),
- {onSearch:actionSearchUser})(SearchUser)
-const CUserEdit = connect((state)=>({}),
-{}
-)(PageAboutUser)
-const Feed = ({aboutMe,onAllFollowing, onPostsFeed, postsFeed}) => {
-console.log('POST FEED', postsFeed)
-return <> 
-<Link className="Feed" to={`/feed`}>
+const CSearch = connect(
+  (state) => ({ searchUser: state.promise?.searchUser?.payload }),
+  { onSearch: actionSearchUser },
+)(SearchUser)
+const CUserEdit = connect((state) => ({}), {})(PageAboutUser)
 
-<Button className="Feed" size="large" onClick={()=>console.log('click')}> Feed </Button>
-</Link>
-</>}
 const Header = () => {
-  const CFeed =connect((state) => ({aboutMe: state.promise?.aboutMe?.payload}))(Feed)
- return  <section className="Header">
-    <CSearch/>
-    {/* <Button icon={<SearchOutlined />}>Search</Button> */}
-    <CFeed/>
-    <AddPost />
-    <Recommendations />
-    <Likes />
-    <CUser />
-  </section>
+  const CFeed = connect((state) => ({
+    aboutMe: state.promise?.aboutMe?.payload,
+  }))(Feed)
+  return (
+    <section className="Header">
+      <CSearch />
+      {/* <Button icon={<SearchOutlined />}>Search</Button> */}
+      <CFeed />
+      <AddPost />
+      <Recommendations />
+      <Likes />
+      <CUser />
+    </section>
+  )
 }
-const Likes = () => <Button size="large" className="Likes"> Likes </Button>
+
+const Likes = () => (
+  <Button size="large" className="Likes">
+    {' '}
+    Likes{' '}
+  </Button>
+)
 
 const Recommendations = () => (
-  <Button size="large" className="Recomendations"> Recommendations </Button>
+  <Button size="large" className="Recomendations">
+    {' '}
+    Recommendations{' '}
+  </Button>
 )
 
 const CBasic = connect(null, { onLoad: actionSetAvatar })(Basic)
 // const CAddPost =connect(null,{actionPostUpsert})
 const User = ({ aboutMe: { _id, login, avatar } = {} }) => (
-
   <Link className="User" to={`/profile/${_id}`}>
-    <Avatar src={ '/' + avatar?.url || user} />
+    <Avatar src={'/' + avatar?.url || user} />
   </Link>
 )
 // backendURL
 const CUser = connect((state) => ({ aboutMe: state.promise.aboutMe?.payload }))(
-  User
+  User,
 )
-  
-//  store.getState().auth?.payload?.sub?.id
 
+//  store.getState().auth?.payload?.sub?.id
 
-const ProtectedRoute = ({roles=[], fallback='/login', component, auth, ...routeProps})=>{
-  console.log('LFLFLFL')
-  const WrapperComponent = (renderProps)=>{
+const ProtectedRoute = ({
+  roles = [],
+  fallback = '/login',
+  component,
+  auth,
+  ...routeProps
+}) => {
+  const WrapperComponent = (renderProps) => {
     // console.log('тут шото ', intersection)
-    const C = component;
-   if (!auth)
-    auth=["anon"]
+    const C = component
+    if (!auth) auth = ['anon']
 
-    let intersection = auth.filter(x => roles.includes(x));
-   if (intersection.length==0)
-    return <Redirect  to={fallback} />
+    let intersection = auth.filter((x) => roles.includes(x))
+    if (intersection.length == 0) return <Redirect to={fallback} />
 
-    return <C {...renderProps}/>
+    return <C {...renderProps} />
   }
-  return <Route {...routeProps} component ={WrapperComponent}/>
+  return <Route {...routeProps} component={WrapperComponent} />
 }
-const PageRegister = ()=>{
-return <div>
-  REGISTER
-</div>}
-const CProtectedRoute = connect(state=>({auth:state.auth?.payload?.sub.acl}))(ProtectedRoute);
-
+const PageRegister = () => {
+  return <div>REGISTER</div>
+}
+const CProtectedRoute = connect((state) => ({
+  auth: state.auth?.payload?.sub.acl,
+}))(ProtectedRoute)
 
- 
 const history = createHistory()
+
 function App() {
-    if (store.getState().auth?.token)
+  if (store.getState().auth?.token) {
+    console.log('токенчик ', store.getState().auth?.payload?.sub?.id)
+
     store.dispatch(actionAboutMe(store.getState().auth?.payload?.sub?.id))
+    store.dispatch(actionAllPosts(store.getState().auth?.payload?.sub?.id))
+  }
+  else {
+    console.log('ошибочка')
+  }
   return (
-
     <Router history={history}>
       <Provider store={store}>
-        
         <div className="App">
           <Header />
           <Divider />
           <Main />
-          <CProtectedRoute roles={["anon"]} fallback="/dashboard"
-           path="/register" component={PageRegister}/>
-           
-
+          <CProtectedRoute
+            roles={['anon']}
+            fallback="/dashboard"
+            path="/register"
+            component={PageRegister}
+          />
 
           {/* <CPostEditor /> */}
           {/* <CPost /> */}

+ 25 - 0
src/App.scss

@@ -88,6 +88,27 @@ main{
   // background-size: 100% 100%;
  
 }
+.MyCarousel{
+  display: block;
+  min-width: 80%;
+  min-height: 80%;
+  background: #c6cece87;
+  border-width: 5px;
+  border-color: rgb(179, 188, 188);
+  border-style: solid;
+  margin-bottom: 40px;
+  margin: 0 10%;
+  border-radius: 5px;
+}
+.PostFeed{
+  border-width: 20;
+  border-color: rgb(138, 138, 220);
+  background: #edf8f89e;
+  border-radius: 5px;
+
+  border-style: solid;
+  margin-bottom: 40px;
+}
 .PostImage{
   // display: flex;
   // align-items: 'center';
@@ -136,4 +157,8 @@ main{
   // padding: 1em;
   overflow-y: scroll;
   // text-align: center;
+}
+.SpoilerButton{
+  right: 30px;
+  margin:20px;
 }

+ 166 - 58
src/actions/index.js

@@ -150,7 +150,7 @@ export const actionAboutMe = () => async (dispatch, getState) => {
   UserFindOne(query:$userId)
   {
     _id createdAt login nick avatar{_id url} 
-     followers{_id login nick} following{_id login nick}
+     followers{_id login nick avatar{_id url}} following{_id login nick avatar{_id url}}
   }
 }`,
         {
@@ -181,38 +181,41 @@ mutation PostUpsert($post:PostInput){
     ),
   )
 
-// export const actionAllPosts = () => async (dispatch, getState) => {
-//   await dispatch(
-//     actionPromise(
-//       'allPosts',
-//       gql(
-//         `query allPosts($userId:String!){
-//   PostFind(query:$userId){
-//            owner{_id} _id title text images{_id url}
-//     }
-// }`,
-//         {
-//           userId: JSON.stringify([
-//             { ___owner: getState().auth?.payload?.sub?.id },
-
-//             { sort: [{ _id: -1 }] },
-//           ]),
-//         },
-//       ),
-//     ),
-//   )
-// }
+export const actionAllPosts = (userId) =>
+    actionPromise(
+      'allPosts',
+      gql(
+        `query allPosts($userId:String!){
+  PostFind(query:$userId){
+           owner{_id} _id title text images{_id url}
+    }
+}`,
+        {
+          userId: JSON.stringify([
+            { ___owner: userId},
+
+            {
+              sort: [{ _id: -1 }],
+              skip: [0],
+              limit: [36]
+            },
 
+          ]),
+        },
+      ),
+    )
 
-export const actionAllPosts = () =>
-    actionPromise('allPosts', 
+export const actionAllPostsFeed = () =>
+    actionPromise('postsFeed', 
     gql(` query allPosts($_id:String){
                 PostFind(query:$_id){
-                  owner{_id} _id title text images{_id url}
+                  owner{_id login avatar{_id url}} _id title text images{_id url}
                 }
             }`, {
         _id: JSON.stringify([{}, {
-            sort: [{ _id: -1 }],
+          sort: [{ _id: -1 }],
+          skip: [0],
+              limit: [36]
         }])
     }))
 
@@ -225,14 +228,25 @@ export const actionOnePost = (_id) => async (dispatch) => {
          PostFindOne(query:$post){
         _id title text images{_id url}
         createdAt
-        comments{_id, createdAt, text 
+        comments{
+          _id, createdAt, text  owner{_id login avatar{_id url}}
+          answers{
+            _id, createdAt, text owner{_id login  avatar{_id url}}
+           
+          }
         owner{_id login avatar{_id url}}}
-        likes{ _id owner{login}}
-        likesCount
+        likes{
+          _id
+          owner{				
+             _id login avatar {url}
+            }
+      }
         }
       }`,
         {
-          post: JSON.stringify([{ _id }]),
+          post: JSON.stringify([{ _id },
+            
+          ]),
         },
       ),
     ),
@@ -275,30 +289,57 @@ export const actionAllFollowing = (_id) => async (dispatch) => {
     ),
   )
 }
-export const actionAddComment = (postId,comment) => async (dispatch) => {
+export const actionAddComment = (postId, text) => async (dispatch) => {
   await dispatch(
     actionPromise(
       'addComment',
       gql(
-        `mutation AddComment($text:String, $postId:ID){
-          CommentUpsert(comment:{
-            post:{
-            _id: $postId
-          } text:$text
-          })
+        `mutation AddComment($comment:CommentInput){
+          CommentUpsert(comment:$comment)
+          {
+            _id
+            text 
+            createdAt
+          }
+        }`,
+        {
+          comment: {
+            post: {
+              _id: postId
+            },
+            text: text
+          },
+        }
+      ),
+    ))
+}
+export const actionAddSubComment = (commentId,comment) => async (dispatch) => {
+  await dispatch(
+    actionPromise(
+      'addSubComment',
+      gql(
+        `mutation AddComment($comment:CommentInput){
+          CommentUpsert(comment:$comment)
           {
             _id
             text 
             createdAt
           }
         }`,
-        {postId:postId,
-          text: comment
+        {
+          comment: {
+      
+            answerTo: {
+              _id: commentId
+            },
+            text:comment
+          }
         },
       ),
     ),
   )
 }
+
 // export const actionAddFullComment = (postId,comment) => async(dispatch) => {
 //   let addComment = await dispatch(actionAddComment(postId,comment));
 //   if(addComment){
@@ -313,6 +354,21 @@ async(dispatch,getState) => {
     promise: {
       addComment: { status },
     },
+} = getState();
+  if(status==="FULFILLED")
+  {
+    await dispatch(actionOnePost(postId));
+  }
+  // await dispatch(actionOnePost(postId));
+  }
+
+  export const actionAddSubFullComment = (postId,commentId,comment) => 
+async(dispatch,getState) => {
+  await dispatch(actionAddSubComment(commentId,comment))
+  const {
+    promise: {
+      addSubComment: { status },
+    },
 } = getState();
   if(status==="FULFILLED")
   {
@@ -338,22 +394,19 @@ export const actionAddLike = (postId) => async (dispatch) => {
     actionPromise(
       'addLike',
       gql(
-        `mutation AddLike($postId:ID){
-          LikeUpsert(like:{
-            post:{
-              _id:$postId
-            }
-            user:{
-              _id:"62361e6492c08631bc4b0e91"
-            }
-          })
+        `mutation AddLike($like:LikeInput){
+          LikeUpsert(like:$like)
           {
             _id owner{_id login}
-            post{_id title text}
           }
         }`,
         {
-          postId:postId
+          like: {
+            post: {
+              _id: postId
+            }
+          }
+          
         } 
       ),
     ),
@@ -374,6 +427,20 @@ async(dispatch,getState) => {
   //  await dispatch(actionOnePost(postId));
 }
 
+export const actionGetFindLiked = (_id) => async (dispatch) => {
+  await dispatch(
+    actionPromise('findLiked', gql(` query LikeFindPost($id:String!) {
+                                        LikeFind(query:$id){
+                                          owner { _id nick login
+                                                avatar{_id url}
+                                    }
+                }
+            } `, {
+      id: JSON.stringify([{ "post._id": _id }])
+    }
+    )))
+}
+
 
 // export const actionDeleteFullLike = (likeId) => async(dispatch,getState) => {
 //   let unLike = await dispatch(actionDeleteLike(likeId));
@@ -382,9 +449,9 @@ async(dispatch,getState) => {
 //   }
 // }
 
-export const actionDeleteFullLike = (likeId) => 
+export const actionDeleteFullLike = (likeId, postId) => 
 async(dispatch,getState) => {
- let unlike =  await dispatch(actionDeleteLike(likeId))
+ await dispatch(actionDeleteLike(likeId,postId))
   const {
     promise: {
       deleteLike: { status },
@@ -392,11 +459,11 @@ async(dispatch,getState) => {
 } = getState();
   if(status==="FULFILLED")
   {
-    await dispatch(actionOnePost(unlike?.post?._id));
+    await dispatch(actionOnePost(postId));
   }
   //  await dispatch(actionOnePost(postId));
 }
-export const actionDeleteLike = (likeId) => async (dispatch) => {
+export const actionDeleteLike = (likeId,postId) => async (dispatch) => {
   await dispatch(
     actionPromise(
       'deleteLike',
@@ -405,13 +472,16 @@ export const actionDeleteLike = (likeId) => async (dispatch) => {
           LikeDelete(like: $like)
           {
             _id, post{
-              _id owner{_id login}
+              _id owner{_id login avatar{_id url}}
             }
           }
         }`,
         {
           like:{
-            _id:likeId
+            _id: likeId,
+            post:{
+                _id: postId
+             }
           }
         }
       ),
@@ -446,7 +516,7 @@ export const actionPostsFeed = () => async (dispatch,getState) => {
             _id likesCount 
             likes{
                 owner{				
-                    login avatar {url}
+                    login avatar {_id url}
                   }
             }
       	}
@@ -458,7 +528,8 @@ export const actionPostsFeed = () => async (dispatch,getState) => {
             {
               sort: [{ _id: -1 }],
               skip: [0],
-               limit: [10],
+              limit: [36]
+              //  limit: [10],
             },
           ]),
         },
@@ -467,6 +538,21 @@ export const actionPostsFeed = () => async (dispatch,getState) => {
   )
 }
 
+export const actionGetAllPosts = (skip) =>
+    actionPromise('allGetPosts', gql(` query allPosts($id:String!){
+                PostFind(query:$id){
+                    _id   images{url _id }
+                }
+            }`, {
+        id: JSON.stringify([{}, {
+            sort: [{ _id: -1 }],
+            skip: [skip || 0],
+            limit: [36]
+        }])
+    }))
+
+    
+
 export const actionSearchUser =(userName)=> async (dispatch)=>{
 
   await dispatch(actionPromise(
@@ -522,3 +608,25 @@ export const actionUserUpdate =
         }
         await dispatch(actionAboutMe());
     };
+
+    export const actionFindSubComment = (findId) =>
+    actionPromise('subComments', gql(`query commentFindOne ($id:String!){
+        CommentFindOne(query:$id){
+       _id text answers { 
+                _id text
+                post {_id }
+                answers { _id}
+                createdAt
+                likes { _id owner {_id avatar{_id url} login nick } }
+                owner {
+                    _id login nick 
+                    avatar { _id url } 
+                    } 
+                }
+        } 
+    }`, {
+      id: JSON.stringify([{
+        _id: findId,
+      }
+      ])
+    }))

+ 87 - 102
src/components/Post.js

@@ -1,29 +1,37 @@
 import { Router, Route, Link, Redirect, Switch } from 'react-router-dom'
-import {actionAllPosts,actionOnePost,actionAddFullComment, actionDeleteFullLike,actionAddFullLike,actionAddLike,actionDeleteLike} from '../actions'
+import {
+  actionAllPosts, actionOnePost, addEmoji, actionAddFullComment, actionGetFindLiked, actionFindSubComment,
+  actionAddSubFullComment, actionDeleteFullLike, actionAddFullLike, actionAddLike, actionDeleteLike
+} from '../actions'
 import photoNotFound from '../materials/photoNotFound.png'
 import { LeftCircleFilled, RightCircleFilled, HeartOutlined,HeartTwoTone,HeartFilled } from '@ant-design/icons'
-import { Carousel,Avatar } from 'antd'
+import { Carousel,Avatar,Tooltip } from 'antd'
 import user from '../materials/user.png'
 import { Provider, connect } from 'react-redux'
 import { Row, Col } from 'antd';
-import { Divider,Input,Button } from 'antd';
+import { Divider, Input, Button } from 'antd';
+import { EditOutlined } from '@ant-design/icons'
+import moment from 'moment';
+import {CComments, AddComment} from '../components/Post_Comment'
+
 import React, { useMemo, useState, useEffect } from 'react'
-const postId="62361ebb92c08631bc4b0e96"
+// const postId="625afa1d069dca48a822ffb0"
 export const Card = ({ post, onPost }) => (
   <>
-    <Link to={`/post/${postId}`} onClick={() => onPost(postId)}>
-    {/* <Link to={`/post/${post?._id}`} onClick={() => onPost(post?._id)}> */}
+    {/* <Link to={`/post/${postId}`} onClick={() => onPost(postId)}> */}
+    {/* {console.log('post id', post?._id)} */}
+    <Link to={`/post/${post?._id}`} onClick={() => onPost(post?._id)}>
       {post?.images && post?.images[0] && post.images[0]?.url ? (
         <img
           className="Card"
           src={ '/' + post.images[0].url}
-          style={{ maxWidth: '200px', maxHeight: '200px' }}
+          style={{minHeight:'150px', minWidth:'150px', maxWidth: '230px', maxHeight: '200px' }}
         />
       ) : (
         <img
           className="Card"
           src={photoNotFound}
-          style={{ maxWidth: '200px', maxHeight: '200px' }}
+          style={{ maxWidth: '230px', minHeight:'150px', minWidth:'150px' ,maxHeight: '200px' }}
         />
       )}
 
@@ -78,45 +86,40 @@ export const MyCarousel = ({ images = [] }) => {
   console.log('IMAGES', images)
   return (
     <>
-      <div style={{
-            display: 'block',
-            minWidth: '80%',
-            minHeight: '80%',
-            background: '#a0a0a0',
-            borderWidth:'10',
-            borderColor: '#000',
-            borderStyle: 'solid',
-            marginBottom: '40px',
-            margin: '0 10%'
-          }}>
+      <div className='MyCarousel'>
         <Carousel
           effect="fade"
           arrows
           nextArrow={<SampleNextArrow />}
           prevArrow={<SamplePrevArrow />}
         >
-          {images &&
-            images.map((i, index) =>
-              i?.url ? (
-                <div key={index}>
-                  <img
-                    className="PostImage"
-                    src={ '/' + i?.url}
-                    style={{
-                      display: 'flex',
-                      alignItems: 'center',
-                      width: '50%',
-                      margin: '0 auto',
-                      maxWidth: '60%',
-                      height:'50%',
-                      minWidth: '40%',
-                      minHeight: '40%',
-                      maxHeight: '60%',
-                      marginBottom: '40px',
-                    }}
-                  />
-                </div>
-              ) : (
+          {
+            images
+              ?
+              (images.map((i, index) =>
+                i?.url && (
+                  <div key={index}>
+                    <img
+                      className="PostImage"
+                      src={'/' + i?.url}
+                      style={{
+                        display: 'flex',
+                        alignItems: 'center',
+                        width: '50%',
+                        margin: '0 auto',
+                        maxWidth: '60%',
+                        height: '50%',
+                        minWidth: '40%',
+                        minHeight: '40%',
+                        maxHeight: '60%',
+                        marginBottom: '40px',
+                      }}
+                    />
+                  </div>
+                )
+              ))
+              
+              : (
                 <div>
                   <img
                     className="PostImage"
@@ -124,44 +127,39 @@ export const MyCarousel = ({ images = [] }) => {
                     style={{ maxWidth: '400px', maxHeight: '400px' }}
                   />
                 </div>
-              ),
-            )}
+              )
+               }
         </Carousel>
       </div>
     </>
   )
 }
-const Comment =({addComment})=>{
-  const [comment, setComment]=useState('')
-  return (
-  <div class="Comments" style={{display: 'flex', flexDirection:'row'}}>
-      <Input size="large" placeholder='Add a comment...' 
-      value={comment} onChange={e=>{setComment(e.target.value)}}/>
-
-      <Button size="large" disabled={comment.length<1} type="primary" onClick={()=>
-        addComment(postId,comment)}> Publish </Button>
-  </div>
-  )
-
-}
-const Like=({addLike, deleteLike, likeId})=>{
-  const [like, setLike]=useState(false)
+const Like = ({ my_Id, findLikes, postId, addLike, deleteLike, likes=[] }) =>
+{
+  const [view, setView] = useState(false);
+  const likeId = likes.find(like => like?.owner?._id === my_Id)?._id
+  const changeLike = () => likeId ? deleteLike(likeId, postId) : addLike(postId)
+  console.log('likeId', likeId)
   return(
     <>
-     {
-        like === true
-        ?
-        // setLike(!like)&&
-       <HeartFilled  style={{ fontSize:'xx-large', color:'red'}} onClick={()=>{
-        deleteLike(likeId)&&setLike(!like)}}/>
-       :
-       <HeartOutlined  style={{ fontSize:'xx-large' }} 
-       onClick={()=>{addLike(postId)&&setLike(!like)}}/>
-       }
+      
+         <span onClick={changeLike}>
+            {likeId ? 
+            <HeartFilled  style={{ fontSize:'xx-large', color:'red'}} />:
+             <HeartOutlined  style={{ fontSize: 'xx-large' }} /> }
+        </span>
+       {likes.length ? 
+         <button style={{ paddingLeft: 8, cursor: 'auto' }} onClick={()=>setView(!view)}> {likes.length} likes
+           
+        </button>
+        :
+        '0 likes'}
+       {view}
     </>
   )
 }
-export const PagePost = ({ onePost,addComment, addLike, deleteLike, aboutMe: { avatar, login } = {}, onPost }) => {
+
+export const PagePost = ({my_Id, onePost,addComment,addCommentReply, addLike, findLikes, findSubComment, deleteLike, aboutMe: { avatar, login } = {}, onPost }) => {
 
  console.log('onePost ', onePost)
   return (
@@ -175,7 +173,7 @@ export const PagePost = ({ onePost,addComment, addLike, deleteLike, aboutMe: { a
             Created Post: {new Intl.DateTimeFormat('en-GB').format(onePost?.createdAt)}
         </h3>
 {/* </div> */}
-</Col>
+    </Col>
 <Col span={12}>
 <div  style={{display: 'flex', flexDirection:'row'}}>
 
@@ -197,38 +195,17 @@ export const PagePost = ({ onePost,addComment, addLike, deleteLike, aboutMe: { a
       <h2> Text: {onePost?.text || ''} </h2>
       <Divider>Comments</Divider>
       <div className="Scroll">
-      
-       {(onePost?.comments ||[]).map(({text, createdAt, owner})=>
-      (
-        <>
-        <div style={{display: 'flex', flexDirection:'row', padding:'5px', margin:'5px' }}>
-         {owner?.avatar ? 
-         <Avatar
-          style={{ width: '25px', height: '25px', marginRight:'2%' }}
-          src={ '/' + owner?.avatar?.url}
-        /> : 
-        <Avatar style={{ width: '25px', height: '25px', marginRight:'2%' }} src={user} />
-      }
-      
-       {owner?.login ? (
-       <h3 style={{marginRight:'2%' , fontWeight: 'bold'}}> {owner?.login} </h3> 
-      ) : (
-        <h3 style={{marginRight:'2%', fontWeight: 'bold'}}> anon </h3>
-          )
-      }
-        <h3 style={{marginRight:'2%'}}>  {text} </h3>
- </div>
-        <p style={{ paddingLeft:'10px'}}>     
-          {new Intl.DateTimeFormat('en-GB').format(createdAt)}
-           </p>
-        </>
 
-        ))
-      }
+            <CComments comments={onePost?.comments || []}/>
      </div>
-     <Like addLike={addLike} deleteLike={deleteLike} likeId={onePost?.likes?._id}/>
+          <Like my_Id={my_Id} findLikes={findLikes} addLike={addLike} deleteLike={deleteLike} likes={onePost?.likes} postId={(onePost?._id)}>
+            {
+              <div>
+          my likes
+              </div>}
+          </Like>
        {/* <HeartTwoTone twoToneColor="#eb2f96" /> */}
-       <Comment addComment={addComment}/>
+          <AddComment addComment={addComment} onePost={onePost}/>
         </Col>
         </Row>
     </>
@@ -237,7 +214,15 @@ export const PagePost = ({ onePost,addComment, addLike, deleteLike, aboutMe: { a
 
 export const CPost = connect((state) => ({
   onePost: state.promise.onePost?.payload,
+  my_Id: state.auth.payload.sub.id || '',
   aboutMe: state.promise?.aboutMe?.payload,
-}), {addComment:actionAddFullComment, 
-    addLike:actionAddFullLike,
-    deleteLike:actionDeleteFullLike})(PagePost)
+  addComment: state.promise?.addComment?.payload,
+  addSubComment: state.promise?.addSubComment,
+
+}), {
+  addLike: actionAddFullLike,
+  findLikes: actionGetFindLiked,
+  deleteLike: actionDeleteFullLike,
+  addComment: actionAddFullComment, 
+  addCommentReply: actionAddSubFullComment,
+})(PagePost)

+ 72 - 0
src/components/PostFeed.js

@@ -0,0 +1,72 @@
+import React, { useMemo, useState, useEffect } from 'react'
+import {
+    actionAllPostsFeed,
+  } from '../actions'
+import { Link} from 'react-router-dom'
+import { Provider, connect } from 'react-redux'
+import { Upload, Button, DatePicker, Space } from 'antd'
+import user from '../materials/user.png'
+import { Avatar, Image, Divider, Radio } from 'antd'
+import { CPost, MyCarousel } from './Post'
+
+const MyPostFeed = ({ postsFeed, onPostsFeed }) => {
+    useEffect(() => {
+      onPostsFeed()
+    }, [])
+  
+    return (
+      <>
+        <h2>Feed</h2>
+            <div>
+                {console.log('POSTFEED', postsFeed)}
+          {
+            (postsFeed || []).map(({ images, title, text, owner }) => (
+              <div className='PostFeed'>
+                {owner?.avatar ? (
+                  <Avatar
+                    style={{ width: '50px', height: '50px' }}
+                    src={'/' + owner?.avatar?.url}
+                  />
+                ) : (
+                  <Avatar style={{ width: '50px', height: '50px' }} src={user} />
+                )}
+                <h1> {owner?.login || 'anon'}</h1>
+  
+                <MyCarousel images={images} style={{ marginTop: '60px' }} />
+                <h1> Title: {title || ''}</h1>
+                <h1> Text: {text || ''}</h1>
+              </div>
+            ))
+          }
+        </div>
+  
+        {/* </div> */}
+  
+        {/* <PagePost onePost={postsFeed}/> <MyCarousel images={postsFeed?.images} />*/}
+      </>
+    )
+  }
+  export const CPostForFeed = connect(
+    (state) => ({
+        postsFeed: state.promise?.postsFeed?.payload,
+    }),
+    { onPostsFeed: actionAllPostsFeed },
+)(MyPostFeed)
+  
+export const Feed = ({ aboutMe, onAllFollowing, onPostsFeed, postsFeed }) => {
+    console.log('POST FEED', postsFeed)
+    return (
+      <>
+        <Link className="Feed" to={`/feed`}>
+          <Button
+            className="Feed"
+            size="large"
+            onClick={() => console.log('click')}
+          >
+            {' '}
+            Feed{' '}
+          </Button>
+        </Link>
+      </>
+    )
+}

+ 208 - 0
src/components/Post_Comment.js

@@ -0,0 +1,208 @@
+import { Router, Route, Link, Redirect, Switch } from 'react-router-dom'
+import {actionAllPosts,actionOnePost,actionAddFullComment,actionGetFindLiked, actionFindSubComment,actionAddSubFullComment,actionDeleteFullLike,actionAddFullLike,actionAddLike,actionDeleteLike} from '../actions'
+import photoNotFound from '../materials/photoNotFound.png'
+import { LeftCircleFilled, RightCircleFilled, HeartOutlined,HeartTwoTone,HeartFilled } from '@ant-design/icons'
+import { Carousel,Avatar,Tooltip } from 'antd'
+import user from '../materials/user.png'
+import { Provider, connect } from 'react-redux'
+import { Row, Col } from 'antd';
+import { Divider, Input, Button } from 'antd';
+import { EditOutlined, SmileOutlined} from '@ant-design/icons'
+import moment from 'moment';
+import React, { useMemo, useState, useEffect } from 'react'
+import 'emoji-mart/css/emoji-mart.css'
+import { Picker } from 'emoji-mart'
+import data from 'emoji-mart/data/google.json'
+import { NimblePicker, Emoji } from 'emoji-mart'
+// import InputEmoji from 'react-input-emoji'; 
+import reactStringReplace from 'react-string-replace'
+// const postId="625afa1d069dca48a822ffb0"
+export const AddComment =({addComment, onePost})=>{
+  const [text, setComment] = useState('')
+  const [showEmojiPicker, setShowEmojiPicker]=useState(false)
+  const addEmoji = ({ colons }) => {
+    setComment((text + ' ' + colons).trim());
+  };
+
+  return (
+
+    // <div style={{ display: 'inline-block' }}>
+
+      <div className="Comments" style={{ display: 'flex',  margin: '0 5px' }}>
+      
+           {
+          showEmojiPicker&& <Picker onSelect={emojiTag => addEmoji(emojiTag)}  set="apple" />
+        }
+ {/* <Button onClick={<Picker  set="apple" />}   // type="link"
+              // shape="circle"
+              // icon="smile"
+          //onSelect={emojiTag => addEmoji(emojiTag)}
+            > smile </Button>
+ */}
+           {/* <InputEmoji style={{ display: 'inline-block' }}
+          value={text}
+          onChange={setComment}
+          cleanOnEnter
+          onEnter={()=>addComment(postId, text)}
+          placeholder="Type a message"
+        />   */}
+        
+
+         <Input style={{ display: 'inline-block' }} size="large" placeholder='Add a comment...' 
+          value={text} onChange={e => { setComment(e.target.value) }}
+        onPressEnter={e => { setComment(e.target.value) }}/> 
+        
+        <Button size="large" disabled={text.length < 1} type="primary"
+          onClick={() => addComment(onePost?._id, text)}> Publish </Button>
+      {console.log('comment ', text )}
+     
+      <SmileOutlined className='smile-btn' style={{ fontSize: 'xx-large' }} 
+          onClick={() => {
+            setShowEmojiPicker(!showEmojiPicker)
+          }}>
+        
+        </SmileOutlined>
+       
+     </div>
+    )
+}
+const SpoilerButton = ({text, close, children }) => {
+    const [opened, setOpened] = useState(close)
+    return (
+      <>
+        <Button 
+          onClick={() => {
+            setOpened(!opened)
+          }}>
+          {text}
+        </Button>
+          {opened && children}
+      </>
+    )
+  }
+  const CommentAuthor = ({owner}) =>
+  <>
+      <div style={{ display: 'flex', flexDirection: 'row', padding: '5px', margin: '5px' }}>
+          {owner?.avatar ?
+            <Avatar
+              style={{ width: '25px', height: '25px',marginRight: '2%'}}
+              src={'/' + owner?.avatar?.url}
+            /> :
+            <Avatar style={{ width: '25px', height: '25px',marginRight: '2%' }} src={user} />
+          }
+          {owner?.login ? (
+            <h3 style={{ marginRight: '2%', fontWeight: 'bold' }}> {owner?.login} </h3>
+          ) : (
+            <h3 style={{ marginRight: '2%', fontWeight: 'bold' }}> anon </h3>
+          )
+      }
+      </div>
+  </>
+
+
+const CommentForReply =({addCommentReply, commentId, onePost})=>{
+    const [comment, setComment]=useState('')
+    return (
+     <>
+      <div style={{display: 'flex', flexDirection: 'row', width:'100%', padding: '15px', marginLeft:'40px'}}>
+          
+        <Input  placeholder='Add a comment...' 
+            value={comment} onChange={e => { setComment(e.target.value) }} />
+        <Button  disabled={comment.length<1} type="primary" onClick={()=>
+            addCommentReply(onePost?._id, commentId, comment)
+          
+          }> Publish </Button>
+          </div>
+      </>
+    )
+  }
+  
+  const CommentText = ({ text,close }) => {
+    const [edit, setEdited] = useState(close)
+    return (
+      <div style={{width:'90%',display:'inline-block', background:'#c8c8c8'}}>
+   <EditOutlined style={{float:'right',  fontSize: 'x-large' }}/>
+        <h3 style={{display:'block'}}>  {reactStringReplace(text, /:(.+?):/g, (match, i) => (
+          <Emoji emoji={match} set='apple' size={20}/> ))}
+        </h3>
+       
+      </div>
+    )
+  }
+  const CommentData = ({ createdAt }) => {
+    return <Tooltip color={'#87d068'} style={{ paddingLeft: '10px' }} title={moment(new Date(+createdAt)).format('lll')} >
+    {moment(new Date(+createdAt)).startOf().fromNow()}
+  </ Tooltip>
+}
+  
+
+  const Comments = ({ comments,onePost, addCommentReply, commentId,children,close,findSubComment }) => {
+    const [opened, setOpened] = useState(close)
+   
+    return (
+      <>
+        {console.log('длина ', comments?.length)}
+       
+        {
+        
+        comments ? (comments).map((comment) =>
+        <>
+      <div style={{display: 'flex', flexDirection: 'row', width:'100%', padding: '15px'}}>
+          <CommentAuthor owner={comment.owner} />
+            <CommentText text={comment.text} />
+            </div>
+            {/* <h3 style={{ marginRight: '2%' }}>  {text} </h3> */}
+            {/* <h3> мой айди {_id}</h3> */}
+          {/* </div> */}
+          <CommentData createdAt={comment.createdAt} />
+          <div style={{display: 'flex', flexDirection: 'row'}}>
+          
+             <div style={{display: 'flex', flexDirection: 'column' }}>
+          <SpoilerButton text={'Reply to'} >
+               {/* <Input />
+               <button> send </button> */}
+                  
+                     
+                  <CommentForReply addCommentReply={addCommentReply} commentId={comment._id} onePost={onePost}/>
+                     
+              </SpoilerButton>
+            </div> 
+          {comment?.answers && comment?.answers?.length ?
+            <div style={{ marginLeft: '10px', display:'inline-block'}}>
+                
+                          <SpoilerButton style={{ position: 'fixed' }} text={'More comments'} onClick={() => findSubComment(commentId)}>
+                              <Comments comments={comment.answers} addCommentReply={addCommentReply} commentId={comment._id} >
+
+                              </Comments>
+                          </SpoilerButton>
+                          
+                      </div>
+                      : null
+            }
+          </div>
+        {/* //   <button onClick={() => {
+        //     setOpened(!opened)
+        //   }}>
+        //   More Comments
+        // </button> && opened && children */}
+        
+          {/* <h3> {answers.map(({ text }) => text)}</h3> */}
+        </>
+      ) : null
+  }
+      </>
+    ) 
+}
+
+export const CComments = connect((state) => ({
+    onePost: state.promise.onePost?.payload,
+    addComment: state.promise?.addComment?.payload,
+    addSubComment: state.promise?.addSubComment,
+  
+}),
+    {
+        addComment: actionAddFullComment, 
+        addCommentReply: actionAddSubFullComment,
+      findSubComment: actionFindSubComment,
+     
+    })(Comments)

+ 108 - 142
src/components/User.js

@@ -1,17 +1,18 @@
-import {actionAllPosts,actionOnePost,
-  actionAboutMe, 
-  actionUploadFile, actionUserUpsert,actionSetAvatar,actionAvatar} from '../actions'
+import {
+  actionAllPosts, actionOnePost, actionAboutMe, actionUploadFile, actionUserUpsert,
+  actionSetAvatar, actionAvatar} from '../actions'
 import user from '../materials/user.png'
 import React, { useMemo, useState, useEffect } from 'react'
-import {Card} from '../components/Post'
+import { Card } from '../components/Post'
 // import {Basic} from '../components/DropZone'
+import { Router, Route, Link, Redirect, Switch } from 'react-router-dom'
 
 import { Provider, connect } from 'react-redux'
 import { Avatar, Image, Divider, Radio } from 'antd'
 import { store } from '../reducers'
 import { useDropzone } from 'react-dropzone'
 import { Upload, Button, DatePicker, Space } from 'antd'
-import { UploadOutlined,SearchOutlined } from '@ant-design/icons'
+import { UploadOutlined, SearchOutlined } from '@ant-design/icons'
 
 export function Basic({ onLoad }) {
   const { acceptedFiles, getRootProps, getInputProps } = useDropzone()
@@ -20,11 +21,10 @@ export function Basic({ onLoad }) {
       {file.path} - {file.size} bytes
     </li>
   ))
-  console.log('acceptedFiles',acceptedFiles)
+  console.log('acceptedFiles', acceptedFiles)
   useEffect(() => {
     acceptedFiles[0] && onLoad(acceptedFiles[0])
-  }
-  , [acceptedFiles])
+  }, [acceptedFiles])
   return (
     <section className="container">
       <div {...getRootProps({ className: 'Dropzone' })}>
@@ -41,19 +41,19 @@ export function Basic({ onLoad }) {
   )
 }
 
-
 export const EditAccount = ({ open, children }) => {
   const [opened, setOpened] = useState(open)
   return (
     <>
-     {/* <Link to={`/editProfile`}> */}
-      <button
+      {/* <Link to={`/editProfile`}> */}
+      <button style={{ width: '100px' }}
         onClick={() => {
           setOpened(!opened)
-        }}>
+        }}
+      >
         Edit account
       </button>
-      {opened && children} 
+      {opened && children}
       {/* </Link> */}
     </>
   )
@@ -67,42 +67,31 @@ const Input = ({ state, onChangeText }) => (
     onChange={onChangeText}
   />
 )
-// const defaultAboutMe = 
-// { avatar:{
-//   url: "images/04488fa15c36b875e2397a10faf3ebe7",
-//   _id: "6230d8368f316d54ac0c38a4"
-// },
-//   createdAt: "1647338874000",
-//   followers: null,
-//   following: null,
-//   login: "Lena",
-//   nick: null,
-//   _id: "6230657a2791790d34ecaecc"}
-const EditInfo =({info={}, onSave, onFileDrop, fileStatus})=>{
+const EditInfo = ({ info = {}, onSave, onFileDrop, fileStatus }) => {
   console.log('filestatus ', fileStatus)
-  const [state,setState]=useState(info)
+  const [state, setState] = useState(info)
   useEffect(() => {
     fileStatus?.status == 'FULFILLED' &&
       setState({
         ...state,
-          ...state.avatar,
-          ...fileStatus.payload
-          // _id: fileStatus?.payload._id,
-          // url: fileStatus?.payload.url
+        ...state.avatar,
+        ...fileStatus.payload,
+        // _id: fileStatus?.payload._id,
+        // url: fileStatus?.payload.url
       })
   }, [fileStatus])
-  
+
   const onChangeLogin = (event) =>
-  setState({
-    ...state,
-    login: event.target.value,
-  })
+    setState({
+      ...state,
+      login: event.target.value,
+    })
   console.log('state my ', state)
   return (
     <section>
       <Basic onLoad={onFileDrop} />
       <Input state={state.login || ''} onChangeText={onChangeLogin} />
-        <h1 className="Title"> LOGIN </h1>
+      <h1 className="Title"> LOGIN </h1>
       <button
         disabled={state?.images?.length == 0}
         onClick={() => onSave(state._id)}
@@ -113,119 +102,96 @@ const EditInfo =({info={}, onSave, onFileDrop, fileStatus})=>{
   )
 }
 export const CEditInfo = connect(
-  (state) => ({ fileStatus: state.promise?.uploadFile}),
+  (state) => ({ fileStatus: state.promise?.uploadFile }),
   {
     onSave: actionAvatar,
     onFileDrop: actionUploadFile,
   },
 )(EditInfo)
-
-const defaultPost={
-      "_id": "62361ebb92c08631bc4b0e96",
-      "title": "Bmw",
-      "text": "Bmw",
-      "images": [
-        {
-          "_id": "62361eb192c08631bc4b0e95",
-          "url": "images/bd9670eb58676ec2f0b5dea9e4d800c6"
-        },
-        {
-          "_id": "62361eb192c08631bc4b0e92",
-          "url": "images/b6527c979360dd52a37d0766e320cf23"
-        },
-        {
-          "_id": "62361eb192c08631bc4b0e93",
-          "url": "images/601b8f0616131b131b36927cb0603944"
-        },
-        {
-          "_id": "62361eb192c08631bc4b0e94",
-          "url": "images/4f47768ed08e4f3df1537bb4bb593e54"
-        }
-      ],
-       "createdAt": "1647713979000"
-    }
 export const PageAboutUser = ({
   aboutMe: { _id, login, nick, createdAt, avatar, followers, following } = {},
-    allPosts,
-    onPosts,
-    onePost,
-    onAboutUser,
-    post={}
-  }) => {
-    useEffect(() => {
-      onAboutUser(_id)
-      // onePost("62361ebb92c08631bc4b0e96")
-        // "62361ebb92c08631bc4b0e96")
-    }, [])
-    // console.log('CREATED AT',new Intl.DateTimeFormat().format(createdAt));
-    return (
-      <section className="AboutMe">
-        <Avatar
-          style={{ marginRight:'20px', width: '150px', height: '150px' }}
-          src={ '/' + avatar?.url || user}
-        />
-        <div className="Info">
-          <h1> {login}</h1>
-          <h3>
-            Created Account: {new Intl.DateTimeFormat('en-GB').format(createdAt)}
-          </h3>
-          <div style={{ display: 'flex' }}>
-            {/* {allPosts?.length} style={{display: 'flex',justifyContent: 'space-between'}}*/}
-            {/* <h3> {allPosts?.length} posts </h3> */}
-  
-            <h3 style={{ marginLeft: '20px' }}>
-              {followers?.length} followers{' '}
-            </h3>
+  allPosts,
+  onPosts,
+  onPost,
+  onePost,
+  onAboutUser,
+  post = {},
+}) => {
+  useEffect(() => {
+    onAboutUser(_id)
+    // onePost(post?._id)
+    // "62361ebb92c08631bc4b0e96")
+  }, [])
+  return (
+    <section className="AboutMe">
+      <Avatar
+        style={{ marginRight: '20px', width: '170px', height: '150px' }}
+        src={'/' + avatar?.url || user}
+      />
+      <div className="Info">
+        <h1> {login}</h1>
+        <h3>
+          Created Account: {new Intl.DateTimeFormat('en-GB').format(createdAt)}
+        </h3>
+        <div style={{ display: 'flex' }}>
+          {allPosts?.length > 0 ? (
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <h3> {allPosts?.length} posts </h3>
+            </div>
+          ) : (
+            <h3> 0 posts </h3>
+          )}
 
-            <h3 style={{ marginLeft: '20px' }}>
-              {following?.length} following{' '}
-            </h3>
-          </div>
-          <h3> nick: {nick == null ? login : nick}</h3>
-         <EditAccount>
-           <div>
-             <h2>Edit login</h2>
-          <p>Edit avatar</p>
-        <CEditInfo/>
-           </div>
-          </EditAccount>
-          <div
-            style={{
-              display: 'flex',
-              flexWrap: 'wrap',
-              padding: '20px',
-              margin: '20px',
-            }}
-          >
-            {/* {(allPosts || [])?.map((item) => (
-              <Card post={item} onPost={onPost} />
-            ))} */}
-          {/* {onePost("62361ebb92c08631bc4b0e96")} */}
-              <Card post={post} onPost={onePost}/>
-          
+          {followers?.length > 0 ? (
+            <Link to={`/followers`}>
+              {' '}
+              <h3 style={{ marginLeft: '20px' }}>
+                {followers.length} followers{' '}
+              </h3>
+            </Link>
+          ) : (
+            <h3 style={{ marginLeft: '20px' }}> 0 followers </h3>
+          )}
+
+          <h3 style={{ marginLeft: '20px' }}>{following?.length} following </h3>
+        </div>
+        <h3> nick: {nick == null ? login : nick}</h3>
+        <EditAccount>
+          <div>
+            <h2>Edit login</h2>
+            <p>Edit avatar</p>
+            <CEditInfo />
           </div>
-          {/* post={item} onPost={onPost} */}
-          {/* <h3> Created Account: {
-              <div>
-              <img
-        src={backendURL + '/' + allPosts?.url}
-        style={{ maxWidth: '200px', maxHeight: '200px' }}/>
-        </div> date} 
-          </h3> */}
+        </EditAccount>
+        <div
+          style={{
+            display: 'flex',
+            flexWrap: 'wrap',
+            padding: '20px',
+            margin: '20px',
+          }}
+        >
+          {(allPosts || [])?.map((item) => (
+            <Card post={item} onPost={onPost} />
+          ))}
         </div>
-      </section>
-    )
-  }
- export const CPageAboutUser = connect(
-    (state) => ({
-      aboutMe: state.promise?.aboutMe?.payload,
-      onePost: state.promise?.onePost?.payload,
-      // post:state.promise?.onePost?.payload,
-      // allPosts: state.promise?.allPosts?.payload,
-    }),
-    { onPosts: actionAllPosts,
-     onAboutUser: actionAboutMe,
-     onLoad:actionUploadFile,
-     onePost:actionOnePost
-    },
-  )(PageAboutUser)
+    
+      </div>
+    </section>
+  )
+}
+export const CPageAboutUser = connect(
+  (state) => ({
+    aboutMe: state.promise?.aboutMe?.payload,
+    allPosts: state.promise?.allPosts?.payload,
+    onePost: state.promise?.onePost?.payload,
+    // post:state.promise?.onePost?.payload,
+    // allPosts: state.promise?.allPosts?.payload,
+  }),
+  {
+    onPosts: actionAllPosts,
+    onAboutUser: actionAboutMe,
+    onLoad: actionUploadFile,
+    onPost: actionOnePost,
+  },
+)(PageAboutUser)