Explorar o código

added custom Rout for Post Page fix render pages Postfeed pofile page

makstravm %!s(int64=3) %!d(string=hai) anos
pai
achega
099dffa043

+ 18 - 12
src/App.js

@@ -3,13 +3,15 @@ import './App.scss';
 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 store from './redux/redux-store';
 import { Authorization } from './components/Authorization';
-import { Content, Main } from './pages/Content';
-import { CProfilePage } from './components/main/Profile';
+import { Container, ContainerLg, Content, Main } from './pages/Content';
+import { CProfilePage } from './pages/ProfilePage';
 import { CAdd } from './components/main/Add';
 import HeaderComponent from './pages/Header';
 import { CMainPostsFeed } from './pages/MainPostsFeed';
+import { CRRoute } from './helpers';
+import { CPostPage } from './pages/PostPage';
 
 export const history = createHistory()
 
@@ -29,15 +31,19 @@ const AppContent = ({ isToken }) =>
 
             <Content>
                 <HeaderComponent />
-                <Main>
-                    <Switch>
-                        <Route path='/' component={CMainPostsFeed} exact />
-                        <Route path='/profile/:_id' component={CProfilePage} />
-                        <Route path='/message' component={Aside} />
-                        <Route path='/add' component={CAdd} />
-                        <Redirect from='/*' to='/' />
-                    </Switch>
-                </Main>
+                <Switch>
+                    <Main>
+                        <Container>
+                            <Route path='/' component={CMainPostsFeed} exact />
+                            <Route path='/profile/:_id' component={CProfilePage} />
+                            <Route path='/message' component={Aside} />
+                            <Route path='/add' component={CAdd} />
+                        </Container>
+                        <CRRoute path='/post/:id' component={CPostPage} />
+                        {/* <Redirect from='/*' to='/' /> */}
+                    </Main>
+                </Switch>
+
             </Content >
 
 

+ 100 - 2
src/App.scss

@@ -49,6 +49,10 @@ select {
     font: inherit;
 }
 
+body {
+    background-color: #f9f9f9;
+}
+
 .Header {
     display: flex;
     background-color: #ececec;
@@ -188,6 +192,7 @@ select {
 .Post {
     padding: 10px 0;
     position: relative;
+
     img {
         margin: 0 auto;
         padding: 1px;
@@ -277,6 +282,7 @@ select {
         }
     }
 }
+
 .Modal {
     .ant-modal-body {
         padding-left: 0;
@@ -285,20 +291,30 @@ select {
     &__inner {
         overflow: auto;
         max-height: 400px;
+        .ant-skeleton-header {
+            padding-left: 20px;
+        }
     }
     li {
         padding-left: 35px;
     }
 }
+
 .Profile {
     width: 100%;
+    padding: 10px;
+    padding-bottom: 30px;
     h1 {
         line-height: 1;
     }
+    &__name {
+        padding-bottom: 10px;
+    }
     &__login {
         color: #8d8d8d;
     }
     &__count {
+        padding-top: 10px;
         strong {
             font-size: 1.2em;
             padding-right: 5px;
@@ -309,7 +325,89 @@ select {
     }
     button {
         color: #000;
-        // padding: 0;
-        // border: none;
     }
+    &__link-message {
+        display: inline-block;
+        background: #1890ff;
+        line-height: 1.5715;
+        box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
+        transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+        height: 32px;
+        padding: 4px 15px;
+        font-size: 14px;
+        border-radius: 2px;
+        color: rgba(0, 0, 0, 0.85);
+        border: 1px solid #1890ff;
+        &:hover {
+            color: #fff;
+            border-color: #40a9ff;
+            background: #40a9ff;
+        }
+    }
+    &__post {
+        padding: 2px;
+        div {
+            padding: 0;
+        }
+        img {
+            width: 100%;
+            height: 160px;
+            margin: 0 auto;
+            object-fit: cover;
+        }
+    }
+    &__box {
+        position: relative;
+        &-icon {
+            position: absolute;
+            top: 0;
+            right: 0;
+        }
+    }
+}
+.PostOne {
+    max-width: 1250px;
+    padding: 0 15px;
+    margin: 0 auto;
+
+    &__inner {
+        display: grid;
+        grid-template-columns: repeat(2, 1fr);
+        grid-template-rows: 0.1fr 1fr;
+        grid-column-gap: 0px;
+        grid-row-gap: 0px;
+        box-shadow: 0 0 6px 2px rgba($color: #000000, $alpha: 0.5);
+        border-radius: 5px;
+    }
+    &__title {
+        grid-area: 1 / 2 / 2 / 3;
+        padding: 10px;
+        background-color: #fff;
+    }
+    &__image {
+        grid-area: 1 / 1 / 3 / 2;
+        width: 60vw;
+        max-width: 850px;
+        background-color: rgb(87, 87, 87);
+        img {
+            max-width: 100%;
+            margin: 0 auto;
+            object-fit: cover;
+            max-height: 850px;
+        }
+        .Post__dots.slick-dots {
+            bottom: 12px;
+        }
+    }
+
+    &__comments {
+        grid-area: 2 / 2 / 3 / 3;
+        background-color: #fff;
+        padding: 10px;
+    }
+    &__aside,
+    &__image {
+    }
+}
+.parent {
 }

+ 28 - 0
src/actions/actionQueries.js

@@ -0,0 +1,28 @@
+export const queries = {
+    "/post/:id": match => ({
+        name: 'postOne',
+        query: `query post($id:String!) {
+                    PostFindOne(query:$id) {
+                        _id createdAt title text 
+                        images{_id url text}
+                        comments {
+                            _id createdAt text 
+                            likes { _id owner {_id}}   
+                            answers{
+                                _id createdAt text 
+                                likes{ _id} 
+                                }
+                            answerTo {
+                                    _id text createdAt
+                                }
+                            }
+                        likes{ _id}
+                        owner {_id login nick
+                            avatar {url}
+                            }
+                        }
+                    }`,
+        variables: { id: JSON.stringify([{ _id: match.params.id }]) }
+    }),
+
+}

+ 32 - 3
src/actions/index.js

@@ -51,6 +51,7 @@ export const actionAboutMe = (id) =>
 
 //*************** Action Posts Feed ******************//
 
+export const actionGetPostAC = (postData) => ({ type: 'GET-POST', newResult: postData })
 
 export const actionAddPostsFeedAC = (postsData, count) => ({ type: 'ADD-POSTS-FEED', newResult: postsData, count })
 export const actionRemovePostsFeedAC = () => ({ type: 'REMOVE-POSTS-FEED' })
@@ -98,8 +99,8 @@ export const actionProfileData = (_id) =>
                             _id  login nick
                             avatar { _id url }     
                             createdAt
-                            followers {_id nick login}
-                            following {_id nick login}
+                            followers {_id }
+                            following {_id }
                 }
             } `, { id: JSON.stringify([{ _id }]) }))
 
@@ -179,6 +180,7 @@ export const actionMyLikePost = (postId) =>
 //****************---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 })
@@ -252,5 +254,32 @@ export const actionGetAvatar = (id) =>
     }`, { myID: JSON.stringify([{ ___owner: id }]) }))
 
 
-//****************---_____________ ---*************************//
+//****************--- Find FOllowing/Follovwrs---*************************//
+
+export const actionFindFollowing = (_id) => ({ type: 'FIND_FOLLOWING', _id })
+export const actionFindFollowers = (_id) => ({ type: 'FIND_FOLLOWERS', _id })
+
+export const actionGetFindFollowing = (_id) =>
+    actionPromise('findFollow', gql(` query findFollowing($id:String!){
+                        UserFindOne(query: $id){
+                            following {
+                                _id nick login
+                                avatar { _id url }
+                            }
+                }
+            } `, { id: JSON.stringify([{ _id }]) }))
+
+export const actionGetFindFollowers = (_id) =>
+    actionPromise('findFollow', gql(` query findFollowers($id:String!){
+                        UserFindOne(query: $id){
+                            followers {
+                                _id  nick login
+                                avatar { _id url }
+                            }
+                }
+            } `, { id: JSON.stringify([{ _id }]) }))
+
+
+
 
+//****************---_____________ ---*************************//

+ 2 - 1
src/components/header/Search.jsx

@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
 import { Link } from 'react-router-dom';
 import { actionFindUsers, actionSearchUsers } from '../../actions';
 import { UserAvatar } from '../../pages/Header';
-
+import { SearchOutlined } from '@ant-design/icons';
 const FindUsersResult = ({ usersRes }) =>
     <div className='Header__search-drop' >
         {
@@ -33,6 +33,7 @@ export const FieldSearch = ({ usersRes, findUsers }) =>
             <Input
                 placeholder="Search users"
                 allowClear
+                prefix={<SearchOutlined style={{ color: '#c9c9c9' }} />}
                 onChange={e => findUsers(e.currentTarget.value)}
             />
         </Popover>

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

@@ -0,0 +1,11 @@
+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 - 160
src/components/main/Profile.js

@@ -1,160 +0,0 @@
-import { Button, Col, List, Row } from 'antd'
-import Modal from 'antd/lib/modal/Modal'
-import React, { useEffect, useState } from 'react'
-import { connect } from 'react-redux'
-import { Link } from 'react-router-dom'
-import { actionProfilePageData, actionRemovePostsFeedAC, actionSubscribe, actionUnSubscribe } from '../../actions'
-import { backURL } from '../../helpers'
-import { UserAvatar } from '../../pages/Header'
-
-const ModalFolower = ({ statusModal, data, title }) => {
-
-    const handleCancel = () => statusModal(false);
-
-    return (
-        <Modal className='Modal'
-            title={title}
-            visible={true}
-            destroyOnClose={true}
-            footer={null}
-            onCancel={handleCancel}>
-            <></>
-            <List className='Modal__inner'
-                itemLayout="horizontal"
-                dataSource={data}
-                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>
-    )
-}
-
-const CModalFollowers = connect(state => ({ data: state?.postsFeed?.userData?.followers || [] }))(ModalFolower)
-const CModalFollowing = connect(state => ({ data: state?.postsFeed?.userData?.following || [] }))(ModalFolower)
-
-
-const ProfileSetting = ({ myID, userId, followers, onSubsuscribe, onUnSubsuscribe }) => {
-    const followCheck = followers.find(f => f._id === myID && true)
-    return (
-        <Col className='Profile__seting' offset={4}>
-            {!!followCheck ?
-                <Button onClick={() => onUnSubsuscribe(userId)}>UnSubscribe</Button> :
-                <Button onClick={() => onSubsuscribe(userId)} type="primary">Subscribe</Button>}
-        </Col>
-    )
-}
-
-const CProfileSetting = connect(state => ({
-    myID: state?.auth?.payload?.sub.id,
-    followers: state?.postsFeed?.userData?.followers || []
-}), { onSubsuscribe: actionSubscribe, onUnSubsuscribe: actionUnSubscribe })(ProfileSetting)
-
-const ProfilePageData = ({ data: { _id, avatar, login, nick, followers, following }, count, setFollowing, setFollowers }) => {
-
-    return (
-        <Row className='Profile' align='middle'>
-            <Col span={8}>
-                <UserAvatar avatarSize={'150px'} avatar={avatar} />
-            </Col>
-            <Col span={14} offset={1}>
-                <Row align='top'>
-                    <Col>
-                        <h1>{nick || login || 'No Name'}</h1>
-                        <span className='Profile__login'>{login || '----'}</span>
-                    </Col>
-                    < CProfileSetting userId={_id} />
-                </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 >
-        </Row >
-    )
-}
-
-const CProfilePageData = connect(state => ({
-    data: state?.postsFeed?.userData || {},
-    count: state?.postsFeed?.count || null
-}))(ProfilePageData)
-
-
-const ProfilePagePosts = ({ posts }) => {
-    return (
-        <Row>
-            {posts.map(p => <Col key={p._id}>
-                {p.images && p.images[0] && p.images[0].url && <img src={(backURL + '/' + p?.images[0].url)} alt='post Img' />}
-            </Col>)
-            }
-        </Row >
-    )
-
-}
-
-export const CProfilePagePosts = connect(state => ({ posts: state.postsFeed?.posts || [] }))(ProfilePagePosts)
-
-const ProfilePage = ({ match: { params: { _id } }, posts, count, 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()
-        }
-    }, [_id])
-
-    useEffect(() => {
-        if (checkScroll) {
-
-            getProfileUser(_id)
-            setCheckScroll(false)
-        }
-    }, [_id, checkScroll])
-
-    const scrollHandler = (e) => {
-        if (e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < 500) {
-            setCheckScroll(true)
-        }
-    }
-
-    return (
-        <>
-            <CProfilePageData setFollowing={setFollowing} setFollowers={setFollowers} />
-            {followers && < CModalFollowers statusModal={setFollowers} title={'Followers'} />}
-            {following && < CModalFollowing statusModal={setFollowing} title={'Following'} />}
-            <CProfilePagePosts />
-        </>
-    )
-}
-
-export const CProfilePage = connect(state => ({
-    posts: state?.postsFeed?.posts || []
-}), { getProfileUser: actionProfilePageData, clearDataProfile: actionRemovePostsFeedAC })(ProfilePage)

+ 2 - 1
src/components/main/postsFeed/PostImage.jsx

@@ -44,7 +44,8 @@ class PostImage extends React.Component {
                 dots={{ className: 'Post__dots' }
                 }>
                 {!!images ?
-                    images.map((i, index) => i?.url ? <div key={i._id}
+                    images.map((i, index) => i?.url ? <div
+                        key={i._id}
                         onMouseEnter={() => this.moveOnDivArray(images.length, index)}
                         onMouseLeave={this.downOnDivArray}>
                         <button onClick={() => this.handlePrev()}

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

@@ -0,0 +1,56 @@
+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 } from '../../../actions';
+import { UserAvatar } from '../../../pages/Header';
+
+
+const ModalFolower = ({ userId, status, statusModal, data, title, follow }) => {
+    const handleCancel = () => statusModal(false);
+
+    useEffect(() => {
+        follow(userId)
+    }, [])
+
+    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={data}
+                    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 => ({
+    userId: state?.postsFeed?.userData?._id,
+    data: state?.promise?.findFollow?.payload?.followers || [],
+    status: state?.promise?.findFollow?.status
+}), { follow: actionFindFollowers })(ModalFolower)
+
+export const CModalFollowing = connect(state => ({
+    userId: state?.postsFeed?.userData?._id,
+    data: state?.promise?.findFollow?.payload?.following || [],
+    status: state?.promise?.findFollow?.status
+}), { follow: actionFindFollowing })(ModalFolower)

+ 24 - 2
src/helpers/index.js

@@ -1,3 +1,8 @@
+import Icon from '@ant-design/icons';
+import { connect } from 'react-redux';
+import { Route } from 'react-router-dom';
+
+
 
 export const jwtDecode = (token) => {
     try {
@@ -6,7 +11,7 @@ export const jwtDecode = (token) => {
         return JSON.parse(base64Token)
     }
     catch (e) {
-        console.log('Лажа, Бро ' + e);
+        console.log('Ой, ошибочка вышла ' + e);
     }
 }
 
@@ -29,4 +34,21 @@ const getGQL = url =>
         return a.data[Object.keys(a.data)[0]]
     }
 
-export const gql = getGQL(backURL + '/graphql');
+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">
+        <path d="M34.8 29.7V11c0-2.9-2.3-5.2-5.2-5.2H11c-2.9 0-5.2 2.3-5.2 5.2v18.7c0 2.9 2.3 5.2 5.2 5.2h18.7c2.8-.1 5.1-2.4 5.1-5.2zM39.2 15v16.1c0 4.5-3.7 8.2-8.2 8.2H14.9c-.6 0-.9.7-.5 1.1 1 1.1 2.4 1.8 4.1 1.8h13.4c5.7 0 10.3-4.6 10.3-10.3V18.5c0-1.6-.7-3.1-1.8-4.1-.5-.4-1.2 0-1.2.6z"></path>
+    </svg>
+
+export const CircularGalleryIcon = props => <Icon component={CircularGallerySvg} {...props} />
+
+const RRoute = ({action, component:Component,...routeProps}) => {
+    const WrapperComponent = (componentProps) => {
+        action(componentProps.match)
+        return <Component {...componentProps}/>
+    }
+    return <Route {...routeProps} component={WrapperComponent}/>
+}
+
+export const CRRoute = connect(null, {action: match => ({type: 'ROUTE', match})})(RRoute)

BIN=BIN
src/images/profile-post-no.jpeg


+ 8 - 3
src/pages/Content.jsx

@@ -4,14 +4,19 @@ import React from 'react'
 
 
 
-export const Main = ({ children }) =>
-    <Row justify='center' className='Main'>
-        <Col xs={{ span: 24 }} sm={{ span: 20 }} md={{ span: 16 }} lg={{ span: 14 }} xl={{span:12}}className='Main__inner'>
+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 }} >
             {children}
         </Col>
     </Row>
 
 
+export const Main = ({ children }) =>
+    <div className='Main'>{children}</div>
+
+
+
 
 export const Content = ({ children }) =>
     <>{children}</>

+ 1 - 1
src/pages/Header.jsx

@@ -35,7 +35,7 @@ export const UserAvatar = ({ avatarSize, avatar }) => {
 const ProfileDropMenu = ({ myID, onLogOut }) =>
     <Menu className='dropMenu'>
         <Menu.Item key={'0'}>
-            <Link to={`${myID}`}><UserOutlined /> My Profile</Link>
+            <Link to={`/profile/${myID}`}><UserOutlined /> My Profile</Link>
         </Menu.Item>
         <Menu.Item key={'1'}>
             <Link to={'/'}><SettingOutlined /> Settings</Link>

+ 27 - 29
src/pages/MainPostsFeed.jsx

@@ -6,21 +6,20 @@ import { UserAvatar } from './Header'
 import Paragraph from 'antd/lib/typography/Paragraph'
 import Text from 'antd/lib/typography/Text'
 import { actionPostsFeed, actionRemovePostsFeedAC } from '../actions'
+import { DateCreated } from '../components/main/DateCreated'
 import PostImage from '../components/main/postsFeed/PostImage'
 import { CPostUserPanel } from '../components/main/postsFeed/PostUserPanel'
 import { CFieldCommentSend } from '../components/main/postsFeed/FieldComment'
 
-const PostTitle = ({ owner }) =>
-    <Link to={`/profile/${owner?._id}`} className='owner'>
-        <Row justify="start" align='middle'>
-            <Col >
-                <UserAvatar avatar={owner?.avatar} login={owner?.login} avatarSize={'45px'} nick={owner?.nick} />
-            </Col>
-            <Col offset={1}>
-                <span>{owner?.nick ? owner.nick : owner?.login ? owner.login : 'Null'}</span>
-            </Col>
-        </Row>
-    </Link >
+export const PostTitle = ({ owner }) =>
+
+    <Row justify="start" align='middle'>
+        <Link to={`/profile/${owner?._id}`} className='owner'>
+            <UserAvatar avatar={owner?.avatar} login={owner?.login} avatarSize={'45px'} nick={owner?.nick} />
+            <span>{owner?.nick ? owner.nick : owner?.login ? owner.login : 'Null'}</span>
+        </Link >
+    </Row>
+
 
 const PostDescription = ({ title, description, date }) =>
     <>
@@ -29,7 +28,9 @@ const PostDescription = ({ title, description, date }) =>
                 {!!title && <Text level={3} strong>{title}</Text>}
             </Col>
             <Col >
-                <Text type='secondary'>{date}</Text>
+                <Text type='secondary'>
+                    <DateCreated date={date} />
+                </Text>
             </Col>
         </Row>
         <Paragraph ellipsis={true ? { rows: 1, expandable: true, symbol: 'more' } : false}>
@@ -55,23 +56,20 @@ const Comments = ({ comments }) =>
         </div>}
     </>
 
-const Post = ({ postData: { _id, text, title, owner, images, createdAt = '', comments, likes } }) => {
-    const date = new Date(createdAt * 1)
-    const resultDate = new Intl.DateTimeFormat('default').format(date)
-    return (
-        <div className='Post'>
-            <Card
-                title={<PostTitle owner={owner} />}
-                cover={<PostImage images={images} />}
-                actions={[<CFieldCommentSend postId={_id} />]}
-            >
-                <CPostUserPanel postId={_id} likes={likes} />
-                <PostDescription title={title} description={text} date={resultDate} />
-                <Comments comments={comments} />
-            </Card>
-        </div>
-    )
-}
+const Post = ({ postData: { _id, text, title, owner, images, createdAt = '', comments, likes } }) =>
+    <div className='Post'>
+        <Card
+            title={<PostTitle owner={owner} />}
+            cover={<PostImage images={images} />}
+            actions={[<CFieldCommentSend postId={_id} />]}
+        >
+            <CPostUserPanel postId={_id} likes={likes} />
+            <PostDescription title={title} description={text} date={createdAt} />
+            <Comments comments={comments} />
+        </Card>
+    </div>
+
+
 
 const MainPostsFeed = ({ posts, count, postsFollowing, postsFollowingRemove, following }) => {
     const [checkScroll, setCheckScroll] = useState(true)

+ 42 - 0
src/pages/PostPage.jsx

@@ -0,0 +1,42 @@
+import React, { useEffect } from 'react'
+import { Row, Col } from 'antd';
+import { connect } from 'react-redux'
+import PostImage from '../components/main/postsFeed/PostImage'
+import { PostTitle } from './MainPostsFeed';
+import { actionRemovePostsFeedAC } from '../actions';
+
+const PostPageAside = ({ data: { owner } }) => {
+
+    return (
+        <PostTitle owner={owner} />
+    )
+}
+
+const CPostPageAside = connect(state => ({ data: state?.postsFeed?.posts || {} }))(PostPageAside)
+
+const PostPage = ({ data: { images } }) => {
+    useEffect(() => {
+        return () => {
+            actionRemovePostsFeedAC()
+        }
+    }, [])
+
+    return (
+        <div className='PostOne'>
+            <div className="PostOne__inner">
+                <div className='PostOne__image'>
+                    <PostImage images={images} />
+                </div>
+                <div className='PostOne__title'>
+                    <CPostPageAside />
+                </div>
+                <div className="PostOne__comments">fjlsdglks</div>
+            </div>
+
+        </div>
+    )
+
+}
+
+export const CPostPage = connect(state => ({ data: state?.postsFeed?.posts || {} }))(PostPage)
+// xs={{ span: 24 }} sm={{ span: 20 }} md={{ span: 16 }} lg={{ span: 16 }}

+ 152 - 0
src/pages/ProfilePage.jsx

@@ -0,0 +1,152 @@
+import React, { useEffect, useState } from 'react'
+import { Button, Card, Col, Row } from 'antd'
+import postNoData from '../images/profile-post-no.jpeg'
+import { connect } from 'react-redux'
+import { Link } from 'react-router-dom'
+import { actionFindFollowers, actionFindFollowing, actionProfilePageData, actionRemovePostsFeedAC, actionSubscribe, actionUnSubscribe } from '../actions'
+import { backURL, CircularGalleryIcon } from '../helpers'
+import { UserAvatar } from './Header'
+import { CModalFollowers, CModalFollowing } from '../components/main/profilePage/ModalFollow'
+import { DateCreated } from '../components/main/DateCreated'
+import Text from 'antd/lib/typography/Text'
+
+
+
+
+
+const ProfileFollow = ({ myID, userId, followers, onSubsuscribe, onUnSubsuscribe }) => {
+    const followCheck = followers.find(f => f._id === myID && true)
+    return (
+        <Col className='Profile__seting' offset={4}>
+            {!!followCheck ?
+                <Button onClick={() => onUnSubsuscribe(userId)}>UnSubscribe</Button> :
+                <Button onClick={() => onSubsuscribe(userId)} type="primary">Subscribe</Button>}
+        </Col>
+    )
+}
+
+const CProfileSetting = connect(state => ({
+    myID: state?.auth?.payload?.sub.id,
+    followers: state?.postsFeed?.userData?.followers || []
+}), { onSubsuscribe: actionSubscribe, onUnSubsuscribe: actionUnSubscribe })(ProfileFollow)
+
+
+const ProfilePageData = ({ data: { _id, avatar, login, nick, createdAt = '', followers, following }, count, setFollowing, setFollowers }) =>
+    <Row className='Profile' >
+        <Col span={8}>
+            <UserAvatar avatarSize={'150px'} avatar={avatar} />
+        </Col>
+        <Col span={14} offset={1}>
+            <Row align='top' className='Profile__name'>
+                <Col>
+                    <h1>{nick || login || 'No Name'}</h1>
+                    <span className='Profile__login'>{login || '----'}</span>
+                </Col>
+                <Col>
+                    <CProfileSetting userId={_id} />
+                </Col>
+            </Row>
+            <Row align='middle'>
+                <Col >
+                    <Text type='secondary'>Account created: <DateCreated date={createdAt} /></Text>
+                </Col>
+                <Col offset={2}>
+                    <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 >
+    </Row >
+
+const CProfilePageData = connect(state => ({
+    data: state?.postsFeed?.userData || {},
+    count: state?.postsFeed?.count || null
+}))(ProfilePageData)
+
+
+const ProfilePagePosts = ({ 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
+                        ?
+                        p.images.length === 1
+                            ?
+                            < img src={(backURL + '/' + p?.images[0].url)} alt='post Img' />
+                            :
+                            <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} />}
+                </Card>
+            </Link>
+
+        </Col>
+        )
+        }
+    </Row >
+
+
+export const CProfilePagePosts = connect(state => ({ posts: state.postsFeed?.posts || [] }))(ProfilePagePosts)
+
+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()
+        }
+    }, [_id])
+
+    useEffect(() => {
+        if (checkScroll) {
+
+            getProfileUser(_id)
+            setCheckScroll(false)
+        }
+    }, [_id, checkScroll])
+
+    const scrollHandler = (e) => {
+        if (e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < 500) {
+            setCheckScroll(true)
+        }
+    }
+
+    return (
+        <>
+            <CProfilePageData setFollowing={setFollowing} setFollowers={setFollowers} />
+            {followers && < CModalFollowers statusModal={setFollowers} title={'Followers'} />}
+            {following && < CModalFollowing statusModal={setFollowing} title={'Following'} />}
+            <CProfilePagePosts />
+        </>
+    )
+}
+
+export const CProfilePage = connect(state => ({
+    posts: state?.postsFeed?.posts || []
+}), { getProfileUser: actionProfilePageData, clearDataProfile: actionRemovePostsFeedAC })(ProfilePage)

+ 3 - 1
src/redux/reducers/myProfile-reducer.js

@@ -8,7 +8,9 @@ export const myProfileReducer = (state = {}, { type, data }) => {
         'ABOUTME-UPDATE-AVATAR': () => {
             return { ...state, avatar: { ...data } }
         },
-      
+        'UPDATE-MY-FOLLOWING': () => {
+            return { ...state, following: [...data] }
+        }
     }
     if (type in types) {
         return types[type]()

+ 4 - 0
src/redux/reducers/postFeed-reducer.js

@@ -10,6 +10,10 @@ export const postsFeedReducer = (state = {}, { type, postId, newResult, userData
                 count
             }
         },
+        'GET-POST': () => {
+            return { ...state, posts: { ...newResult } }
+
+        },
         'ADD-PROFILE-DATA': () => {
             return {
                 ...state,

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

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

+ 2 - 0
src/redux/redux-store.js

@@ -6,6 +6,7 @@ import { promiseReducer } from './reducers/promise-reducer';
 import createSagaMiddleware from 'redux-saga'
 import { rootSaga } from './saga';
 import { actionFullAboutMe } from '../actions'
+import { routeReducer } from './reducers/route-reducer';
 
 const sagaMiddleware = createSagaMiddleware()
 
@@ -14,6 +15,7 @@ const store = createStore(combineReducers({
     promise: promiseReducer,
     myData: myProfileReducer,
     postsFeed: postsFeedReducer,
+    route: routeReducer,
 }),
     applyMiddleware(sagaMiddleware))
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 56 - 20
src/redux/saga/index.js