فهرست منبع

added top part page profile, modal followind and followers

makstravm 2 سال پیش
والد
کامیت
c8aa80b872

+ 14 - 8
src/App.js

@@ -8,6 +8,7 @@ import { Authorization } from './components/Authorization';
 import { Content, Main } from './pages/Content';
 import HeaderComponent from './components/header/Header';
 import { CMainPostFeed } from './components/main/MainPostFeed.js';
+import { CLoginForm, CProfilePage } from './components/main/Profile';
 
 export const history = createHistory()
 
@@ -24,16 +25,21 @@ const AppContent = ({ isToken }) =>
                 <Redirect from='/*' to='/auth/login' />
             </Switch>
             :
-            <Switch>
-                <Content>
-                    <HeaderComponent />
-                    <Main>
+
+            <Content>
+                <HeaderComponent />
+                <Main>
+                    <Switch>
                         <Route path='/' component={CMainPostFeed} exact />
+
+                        <Route path='/profile/:_id' component={CProfilePage} />
+
                         <Route path='/message' component={Aside} />
-                        <Redirect from='/*' to='/' />
-                    </Main>
-                </Content >
-            </Switch>
+                        <Redirect from='/*' to='/profile/614c8ef4f9fc3a5e42bddb28' />
+                    </Switch>
+                </Main>
+            </Content >
+
 
             // <Switch>
             //     <Route path='/' component={Content} exact />

+ 36 - 1
src/App.scss

@@ -141,7 +141,6 @@ select {
     }
 }
 
-
 .Authorization {
     height: 100%;
     background-position: right top;
@@ -278,3 +277,39 @@ select {
         }
     }
 }
+.Modal {
+    .ant-modal-body {
+        padding-left: 0;
+        padding-right: 0;
+    }
+    &__inner {
+        overflow: auto;
+        max-height: 400px;
+    }
+    li {
+        padding-left: 35px;
+    }
+}
+.Profile {
+    width: 100%;
+    h1 {
+        line-height: 1;
+    }
+    &__login {
+        color: #8d8d8d;
+    }
+    &__count {
+        strong {
+            font-size: 1.2em;
+            padding-right: 5px;
+        }
+        span {
+            font-size: 1.2em;
+        }
+    }
+    button {
+        color: #000;
+        padding: 0;
+        border: none;
+    }
+}

+ 29 - 2
src/actions/index.js

@@ -10,11 +10,15 @@ export const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJE
 export const actionAuthLogin = (token, remember) => ({ type: 'AUTH_LOGIN', token, remember })
 export const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })
 
-export const actionAddPostsFeedAC = (addPosts, myLikes) => ({ type: 'ADD-POST-FEED', addPosts, myLikes })
+export const actionAddPostsFeedAC = (newResult) => ({ type: 'ADD-POST-FEED', newResult })
+export const actionRemovePostsFeedAC = () => ({ type: 'REMOVE-POST-FEED' })
+
 export const actionAddLikePostAC = (postId, newResult) => ({ type: 'ADD-POST-LIKE', postId, newResult })
 export const actionRemoveLikePostAC = (postId, newResult) => ({ type: 'REMOVE-POST-LIKE', postId, newResult })
 export const actionAddCommentAC = (postId, newResult) => ({ type: 'ADD-COMMENT', postId, newResult })
 
+export const actionAddProfileDataAC = (userData, userPosts) => ({ type: 'ADD-PROFILE-DATA', userData, userPosts })
+
 //****************---Action Authirization ---*************************//
 
 export const actionLogin = (login, password) =>
@@ -35,7 +39,6 @@ export const actionProfilData = (_id) =>
                         UserFindOne(query: $id){
                             _id  login nick
                             avatar { _id url }
-                            following {_id} 
                   }
                 }`, { id: JSON.stringify([{ ___owner: _id }]) }))
 
@@ -97,3 +100,27 @@ export const actionFindComment = (postId) =>
         }
     }`, { id: JSON.stringify([{ _id: postId }]) }))
 
+//****************---Action ProfileData ---*************************//
+
+export const actionUserData = (_id) =>
+    actionPromise('userOneData', gql(` query userOned($id:String!){
+                        UserFindOne(query: $id){
+                            _id  login nick
+                            avatar { _id url }     
+                            createdAt
+                            followers {_id nick login}
+                            following {_id nick login}
+                }
+            } `, { id: JSON.stringify([{ _id }]) }))
+
+export const actionUserPost = (_id) =>
+    actionPromise('userOneData', gql(` query userOned($id:String!){
+                PostFind(query:$id){
+                    _id   images{url _id}
+                }
+                }`, { id: JSON.stringify([{ ___owner: _id }]) }))
+
+
+//****************---Action ProfileData ---*************************//
+
+// export const actionSubscribe=()

+ 12 - 20
src/components/header/Header.jsx

@@ -1,5 +1,6 @@
 import React from 'react'
 import logo from '../../logo.svg';
+import noAva from '../../images/noAva.png'
 import { Link } from 'react-router-dom';
 import { CFieldSearch } from './Search';
 import { connect } from 'react-redux';
@@ -16,31 +17,22 @@ const UserNav = ({ id, profileData }) => {
     </div>
 }
 
-export const UserAvatar = ({ avatarSize, avatar, login = "user", nick }) => {
+export const UserAvatar = ({ avatarSize, avatar}) => {
     return (
         <>
-            {
-                avatar && avatar?.url ?
-                    <Avatar style={{
-                        width: avatarSize,
-                        height: avatarSize
-                    }}
-                        src={
-                            <img src={(backURL + '/' + avatar.url)} alt='avatar' />
-                        } /> :
-                    <Avatar style={{
-                        width: avatarSize,
-                        height: avatarSize,
-                        color: '#040c80',
-                        backgroundColor: '#f8cff0'
-                    }}>
-                        <span style={{ fontWeight: 500, fontSize: '1.3em', lineHeight: avatarSize }}>{nick ? nick[0].toUpperCase() : login ? login[0].toUpperCase() : 'U'}
-                        </span>
-                    </Avatar >
-            }
+            <Avatar style={{
+                width: avatarSize,
+                height: avatarSize
+            }}
+                src={avatar && avatar?.url ?
+                    <img src={(backURL + '/' + avatar.url)} alt='avatar' /> :
+                    <img src={noAva} />
+                } />
+
         </>
     )
 }
+
 const ProfileDropMenu = ({ onLogOut }) =>
     <Menu className='dropMenu'>
         <Menu.Item key={'0'}>

+ 5 - 3
src/components/main/MainPostFeed.js

@@ -10,9 +10,10 @@ import Paragraph from 'antd/lib/typography/Paragraph'
 import Text from 'antd/lib/typography/Text'
 import TextArea from 'antd/lib/input/TextArea'
 import { actionAddPostsFeed, actionFullAddComment, actionFullAddLikePost, actionFullRemoveLikePost } from '../../redux/redux-thunk'
+import { actionRemovePostsFeedAC } from '../../actions'
 
 const PostTitle = ({ owner }) =>
-    <Link to={`/${owner?._id}`} className='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} />
@@ -222,9 +223,8 @@ const Post = ({ postData: { _id, text, title, owner, images, createdAt, comments
     )
 }
 
-const MainPostFeed = ({ posts, postsFollowing }) => {
+const MainPostFeed = ({ posts, postsFollowing, postsFollowingRemove }) => {
     const [checkScroll, setCheckScroll] = useState(true)
-
     useEffect(async () => {
         if (checkScroll) {
             await postsFollowing(posts.length)
@@ -236,6 +236,7 @@ const MainPostFeed = ({ posts, postsFollowing }) => {
         document.addEventListener('scroll', scrollHandler)
         return () => {
             document.removeEventListener('scroll', scrollHandler)
+            postsFollowingRemove()
         }
     }, [])
 
@@ -255,4 +256,5 @@ export const CMainPostFeed = connect(state => ({
     posts: state?.postsFeed?.posts || []
 }), {
     postsFollowing: actionAddPostsFeed,
+    postsFollowingRemove: actionRemovePostsFeedAC,
 })(MainPostFeed)

+ 105 - 8
src/components/main/Profile.js

@@ -1,23 +1,120 @@
-import React from 'react'
+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 } from '../../redux/redux-thunk'
+import { UserAvatar } from '../header/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?.profileData?.userData?.followers || [] }))(ModalFolower)
+const CModalFollowing = connect(state => ({ data: state?.profileData?.userData?.following || [] }))(ModalFolower)
 
 
+const ProfileSetting = ({myID}) => {
+  useEffect(() => {
+      
+      return () => {
+          
+      }
+  }, [])
+    return (
+        <Col className='Profile__seting' offset={4}>
+            <Button type="primary">Primary Button</Button>
+        </Col>
+    )
+}
 
+const CProfileSetting = connect(state => ({ myID: state?.auth?.payload.sub.id,
+ }))(ProfileSetting)
 
+const ProfilePageData = ({ data: { _id, avatar, login, nick, followers, following }, posts, setFollowing, setFollowers }) => {
 
-// export CProfile= connect()(Profile)
+    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 />
+                </Row>
+                <Row className='Profile__count' align='middle' justify='space-between'>
+                    <Col >
+                        <strong>{posts?.length || '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 >
+    )
+}
 
-export const Profile = () => {
+const CProfilePageData = connect(state => ({
+    data: state?.profileData?.userData || {},
+    posts: state?.profileData?.userPosts || []
+}))(ProfilePageData)
+
+const ProfilePage = ({ match: { params: { _id, id } }, getProfileUser }) => {
+    const [followers, setFollowers] = useState(false)
+    const [following, setFollowing] = useState(false)
     useEffect(() => {
-        
-    }, [])
+        getProfileUser(_id)
+    }, [_id])
     return (
-        <div>
-            
-        </div>
+        <>
+            <CProfilePageData setFollowing={setFollowing} setFollowers={setFollowers} />
+            {followers && < CModalFollowers statusModal={setFollowers} title={'Followers'} />}
+            {following && < CModalFollowing statusModal={setFollowing} title={'Following'} />}
+        </>
     )
 }
+
+export const CProfilePage = connect(null, { getProfileUser: actionProfilePageData })(ProfilePage)

BIN
src/images/noAva.png


+ 14 - 8
src/redux/postFeed-reducer.js

@@ -2,13 +2,19 @@ import React from 'react'
 import { gql } from '../helpers'
 import { actionPromise } from './redux-thunk'
 
-export const postFeedReducer = (state = {}, { type, addPosts, newLike, postId, likeId, newResult }) => {
+export const postFeedReducer = (state = {}, { type, postId, newResult }) => {
     const { posts } = state
     const types = {
         'ADD-POST-FEED': () => {
             return {
                 ...state,
-                posts: !!posts ? [...posts, ...addPosts] : [...addPosts]
+                posts: !!posts ? [...posts, ...newResult] : [...newResult]
+            }
+        },
+        'REMOVE-POST-FEED': () => {
+            return {
+                ...state,
+                posts: []
             }
         },
         'ADD-POST-LIKE': () => {
@@ -58,10 +64,10 @@ export const actionMyFolowisgPosts = (skip) =>
             createdAt
         }
     }`, {
-                query: JSON.stringify([{},
-                {
-                    sort: [{ _id: -1 }],
-                    skip: [skip +735],
-                    limit: [10]
-                }])
+            query: JSON.stringify([{},
+            {
+                sort: [{ _id: -1 }],
+                skip: [skip +743 ],
+                limit: [10]
+            }])
         }))

+ 15 - 0
src/redux/profile-reducer.js

@@ -0,0 +1,15 @@
+import React from 'react'
+
+export const profileReducer = (state = {}, { type, userData, userPosts }) => {
+    const types = {
+        'ADD-PROFILE-DATA': () => {
+            return {
+                ...state, userData, userPosts
+            }
+        }
+    }
+    if (type in types) {
+        return types[type]()
+    }
+    return state
+}

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

@@ -1,12 +1,14 @@
 import { createStore, combineReducers, applyMiddleware } from 'redux';
 import thunk from 'redux-thunk';
 import { authReducer } from './auth-reducer';
-import { postFeedReducer } from './postFeed-reducer';
+import { postFeedReducer } from './post-reducer';
+import { profileReducer } from './profile-reducer';
 import { promiseReducer } from './promise-reducer';
 
 export const store = createStore(combineReducers({
     promise: promiseReducer,
     auth: authReducer,
-    postsFeed: postFeedReducer
+    postsFeed: postFeedReducer,
+    profileData:profileReducer
 }),
     applyMiddleware(thunk))

+ 77 - 0
src/redux/redux-thunk.js

@@ -0,0 +1,77 @@
+import { actionAddComment, actionAddCommentAC, actionAddLikePost, actionAddLikePostAC, actionAddPostsFeedAC, actionAuthLogin, actionFindComment, actionLogin, actionMyLikePost, actionPending, actionRegister, actionRejected, actionRemoveLikePost, actionRemoveLikePostAC, actionResolved, actionUserData, actionUserPost, actionAddProfileDataAC } from "../actions"
+import { actionMyFolowisgPosts } from "./post-reducer"
+
+export const actionPromise = (name, promise) =>
+    async dispatch => {
+        dispatch(actionPending(name))
+        try {
+            let data = await promise
+            dispatch(actionResolved(name, data))
+            return data
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }
+
+export const actionFullLogin = (login, password, remember) =>
+    async dispatch => {
+        let token = await dispatch(actionLogin(login, password))
+        if (token) {
+            dispatch(actionAuthLogin(token, remember))
+        }
+    }
+
+export const actionFullRegister = (login, password, remember) =>
+    async dispatch => {
+        await actionRegister(login, password)
+        let token = await dispatch(actionLogin(login, password))
+        if (token) {
+            dispatch(actionAuthLogin(token, remember))
+        }
+    }
+
+export const actionFullRemoveLikePost = (likeId, postId) =>
+    async dispatch => {
+        await actionRemoveLikePost(likeId)
+        const { likes } = await dispatch(actionMyLikePost(postId))
+        console.log(likes);
+        if (likes) {
+            dispatch(actionRemoveLikePostAC(postId, likes))
+        }
+    }
+
+export const actionFullAddLikePost = (postId) =>
+    async dispatch => {
+        await actionAddLikePost(postId)
+        const { likes } = await dispatch(actionMyLikePost(postId))
+        if (likes) {
+            dispatch(actionAddLikePostAC(postId, likes))
+        }
+    }
+
+export const actionAddPostsFeed = (skip) =>
+    async dispatch => {
+        let posts = await dispatch(actionMyFolowisgPosts(skip))
+        if (posts) {
+            dispatch(actionAddPostsFeedAC(posts))
+        }
+    }
+
+export const actionFullAddComment = (postId, text) =>
+    async dispatch => {
+        await actionAddComment(postId, text)
+        const { comments } = await dispatch(actionFindComment(postId))
+        if (comments) {
+            dispatch(actionAddCommentAC(postId, comments))
+        }
+    }
+
+export const actionProfilePageData = (id) =>
+    async dispatch => {
+        const userData = await dispatch(actionUserData(id))
+        const userPosts = await dispatch(actionUserPost(id))
+        if (userData, userPosts) {
+            dispatch(actionAddProfileDataAC(userData, userPosts))
+        }
+    }