Jelajahi Sumber

added modal liked

makstravm 2 tahun lalu
induk
melakukan
608409c066

+ 2 - 0
package.json

@@ -8,11 +8,13 @@
         "@testing-library/react": "^12.1.2",
         "@testing-library/user-event": "^13.5.0",
         "antd": "^4.18.2",
+        "array-move": "^4.0.0",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "react-redux": "^7.2.6",
         "react-router-dom": "^5.3.0",
         "react-scripts": "5.0.0",
+        "react-sortable-hoc": "^2.0.0",
         "redux": "^4.1.2",
         "redux-saga": "^1.1.3",
         "sass": "^1.45.0",

+ 0 - 3
src/App.js

@@ -31,7 +31,6 @@ const AppContent = ({ isToken }) =>
 
             <Content>
                 <HeaderComponent />
-                <Switch>
                     <Main>
                         <Container>
                             <Route path='/' component={CMainPostsFeed} exact />
@@ -42,8 +41,6 @@ const AppContent = ({ isToken }) =>
                         <CRRoute path='/post/:id' component={CPostPage} />
                         {/* <Redirect from='/*' to='/' /> */}
                     </Main>
-                </Switch>
-
             </Content >
 
 

+ 14 - 1
src/App.scss

@@ -185,7 +185,7 @@ body {
 .Main {
     padding-top: 58px;
     &__inner {
-        padding-top: 15px;
+        padding-top: 22px;
     }
 }
 .owner .nick {
@@ -283,6 +283,19 @@ body {
             box-shadow: none;
         }
     }
+    &__panel-btn {
+        button {
+            cursor: pointer;
+            border: none;
+            padding: 0;
+            margin: 0;
+            background-color: inherit;
+            transition: all 0.3s;
+            &:hover {
+                text-shadow: 1px 1px 3px #000;
+            }
+        }
+    }
 }
 
 .Modal {

+ 3 - 0
src/actions/actionQueries.js

@@ -8,6 +8,9 @@ export const queries = {
                         comments {
                             _id createdAt text 
                             likes { _id owner {_id}}   
+                            owner {_id login nick
+                                    avatar {url}
+                                }
                             answers{
                                 _id createdAt text 
                                 likes{ _id} 

+ 10 - 2
src/actions/index.js

@@ -180,7 +180,7 @@ export const actionMyLikePost = (postId) =>
 //****************---Action Subscribe ---*************************//
 
 
-export const actionUpdateMyFollowingAC = (data) => ({type:'UPDATE-MY-FOLLOWING', data})
+export const actionUpdateMyFollowingAC = (data) => ({ type: 'UPDATE-MY-FOLLOWING', data })
 export const actionUpdateFollowersAC = (newResult) => ({ type: 'UPDATE-FOLLOWERS', newResult })
 
 export const actionSubscribe = (userId) => ({ type: 'SUBSCRIBE', userId })
@@ -258,6 +258,7 @@ export const actionGetAvatar = (id) =>
 
 export const actionFindFollowing = (_id) => ({ type: 'FIND_FOLLOWING', _id })
 export const actionFindFollowers = (_id) => ({ type: 'FIND_FOLLOWERS', _id })
+export const actionFindLiked = (_id) => ({ type: 'FIND_LIKED', _id })
 
 export const actionGetFindFollowing = (_id) =>
     actionPromise('findFollow', gql(` query findFollowing($id:String!){
@@ -279,7 +280,14 @@ export const actionGetFindFollowers = (_id) =>
                 }
             } `, { id: JSON.stringify([{ _id }]) }))
 
-
+export const actionGetFindLiked = (_id) =>
+    actionPromise('findLiked', gql(` query LikeFindPost($id:String!) {
+                                        LikeFind(query:$id){
+                                          owner { _id nick login
+                                                avatar{_id url}
+                                    }
+                }
+            } `, { id: JSON.stringify([{ "post._id": _id }]) }))
 
 
 //****************---_____________ ---*************************//

+ 3 - 2
src/components/main/Add.js

@@ -3,7 +3,7 @@ import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
 import { connect } from 'react-redux';
 import { Upload, message } from 'antd';
 import { backURL, gql } from '../../helpers';
-import { Loo } from './Loo';
+import { Adafghh, Loo } from './Loo';
 import { actionUpdateAvatar } from '../../actions';
 
 const Add = ({ imageUrl, onUploadFile }) => {
@@ -50,9 +50,10 @@ const Add = ({ imageUrl, onUploadFile }) => {
             <hr />
             <hr />
             <Loo />
+            <Adafghh />
         </>
 
     )
 }
 
-export const CAdd = connect(state => ({ imageUrl: state?.myData?.avatar?.url }), { onUploadFile: actionUpdateAvatar  })(Add)
+export const CAdd = connect(state => ({ imageUrl: state?.myData?.avatar?.url }), { onUploadFile: actionUpdateAvatar })(Add)

+ 62 - 11
src/components/main/Loo.js

@@ -1,11 +1,13 @@
 import { Upload, Modal } from 'antd';
 import { PlusOutlined } from '@ant-design/icons';
-import React from 'react';
 import { backURL } from '../../helpers';
+import React, { Component } from 'react';
+import { render } from 'react-dom';
+import { arrayMove, sortableContainer, sortableElement } from 'react-sortable-hoc';
 
 function getBase64(file) {
     return new Promise((resolve, reject) => {
-   
+
         const reader = new FileReader();
         reader.readAsDataURL(file);
         reader.onload = () => resolve(reader.result);
@@ -60,7 +62,8 @@ export class Loo extends React.Component {
 
     handleCancel = () => this.setState({ previewVisible: false });
 
-    handlePreview = async file => {
+    handlePreview = async (file, fileList) => {
+        console.log(fileList);
         if (!file.url && !file.preview) {
             file.preview = await getBase64(file.originFileObj);
         }
@@ -72,8 +75,21 @@ export class Loo extends React.Component {
         });
     };
 
-    handleChange = ({ fileList }) => this.setState({ fileList });
-
+    handleChange = ({ file, fileList }) => {
+        if (file.status === 'uploading') {
+            // setLoading(true)
+        }
+        if (file.status === 'done') {
+            console.log(fileList);
+            // message.success(`${file.name} file uploaded successfully`);
+            // await onUploadFile(file.response)
+            // setImageLoad(true)
+            // setLoading(false)
+        } else if (file.status === 'error') {
+            // message.error(`${file.name} file upload failed.`);
+        }
+        this.setState({ fileList });
+    }
     render() {
         const { previewVisible, previewImage, fileList, previewTitle } = this.state;
         const uploadButton = (
@@ -82,28 +98,63 @@ export class Loo extends React.Component {
                 <div style={{ marginTop: 8 }}>Upload</div>
             </div>
         );
+        const props = {
+            name: 'photo',
+            action: `${backURL}/upload`,
+            headers: localStorage.authToken || sessionStorage.authToken ? { Authorization: 'Bearer ' + (localStorage.authToken || sessionStorage.authToken) } : {}
+        }
         return (
             <>
-                <Upload
-                name='photo'
-                    action={`${backURL}/upload`}
+                <Upload {...props}
                     listType="picture-card"
-                    fileList={fileList}
+                    multiple={true}
                     onPreview={this.handlePreview}
                     onChange={this.handleChange}
                 >
                     {fileList.length >= 8 ? null : uploadButton}
                 </Upload>
-                <Modal
+                {/* <Modal
                     visible={previewVisible}
                     title={previewTitle}
                     footer={null}
                     onCancel={this.handleCancel}
                 >
                     <img alt="example" style={{ width: '100%' }} src={previewImage} />
-                </Modal>
+                </Modal> */}
             </>
         );
     }
 }
 
+
+const SortableItem = sortableElement(({ value }) => <li>{value}</li>);
+
+const SortableContainer = sortableContainer(({ children }) => {
+    return <ul>{children}</ul>;
+});
+
+export class Adafghh extends Component {
+    state = {
+        items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
+    };
+
+    onSortEnd = ({ oldIndex, newIndex }) => {
+        this.setState(({ items }) => ({
+            items: arrayMove(items, oldIndex, newIndex),
+        }));
+    };
+
+    render() {
+
+        const { items } = this.state;
+
+        return (
+            <SortableContainer onSortEnd={this.onSortEnd} >
+                {items.map((value, index) => (
+                    <SortableItem key={`item-${value}`} index={index} axis="xy" value={value} />
+                ))}
+            </SortableContainer>
+        );
+    }
+}
+

+ 15 - 17
src/components/main/postsFeed/PostUserPanel.jsx

@@ -3,7 +3,7 @@ import { Button, Col, Row, Tooltip } from "antd"
 import { useState } from "react"
 import { connect } from "react-redux"
 import { actionDelLikePost, actionLikePost } from "../../../actions"
-import { CModalLikes } from "../profilePage/ModalFollow"
+import { CModalPostLiked } from "../profilePage/ModalFollow"
 
 
 const HeartLike = ({ styleFontSize, likeStatus, changeLike }) =>
@@ -12,9 +12,10 @@ const HeartLike = ({ styleFontSize, likeStatus, changeLike }) =>
         type="none"
         shape="circle"
         icon={
-            likeStatus ?
-                <HeartFilled style={{ color: '#ff6969', fontSize: `${styleFontSize}` }} /> :
-                <HeartOutlined style={{ color: '#1890ff', fontSize: `${styleFontSize}` }} />}
+            likeStatus
+                ? <HeartFilled style={{ color: '#ff6969', fontSize: `${styleFontSize}` }} />
+                : <HeartOutlined style={{ color: '#1890ff', fontSize: `${styleFontSize}` }} />
+        }
     />
 
 const PostUserPanel = ({ myID, postId = '', likes = [], styleFontSize, addLikePost, removeLikePost }) => {
@@ -31,23 +32,20 @@ const PostUserPanel = ({ myID, postId = '', likes = [], styleFontSize, addLikePo
     })
 
     const changeLike = () => likeStatus ? removeLikePost(likeId, postId) : addLikePost(postId)
-    const text = () => !!likes.length &&`Likes: ${likes.length}`
 
 
     return (
         <>
-            <Row className="Post__panel-btn">
-                <Tooltip title={text} >
-                    <Col className='Post__heart'>
-                        <HeartLike
-                            changeLike={changeLike}
-                            likeStatus={likeStatus}
-                            styleFontSize={styleFontSize} />
-                    </Col>
-                </Tooltip>
-
-                <Col>
-
+            {open && <CModalPostLiked statusModal={setOpen} title={'Liked'} id={postId} />}
+            <Row className="Post__panel-btn" align="middle">
+                <Col className='Post__heart'>
+                    <HeartLike
+                        changeLike={changeLike}
+                        likeStatus={likeStatus}
+                        styleFontSize={styleFontSize} />
+                </Col>
+                <Col offset={0.5}>
+                    {!!likes.length && <button onClick={() => { setOpen(true) }}>Likes:<strong>{` ${likes.length}`}</strong></button>}
                 </Col>
             </Row>
         </>

+ 9 - 3
src/components/main/profilePage/ModalFollow.js

@@ -3,7 +3,7 @@ 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 { actionFindFollowers, actionFindFollowing, actionFindLiked } from '../../../actions';
 import { UserAvatar } from '../../../pages/Header';
 
 
@@ -13,7 +13,7 @@ const ModalFolower = ({ id, status, statusModal, data, title, follow }) => {
     useEffect(() => {
         follow(id)
     }, [])
-
+    const newData = data.map(d => d.owner ? d.owner : d)
     return (
         <Modal className='Modal'
             title={title}
@@ -25,7 +25,7 @@ const ModalFolower = ({ id, status, statusModal, data, title, follow }) => {
                 ? <Skeleton className='Modal__inner' avatar active paragraph={{ rows: 0 }} />
                 : <List className='Modal__inner'
                     itemLayout="horizontal"
-                    dataSource={data}
+                    dataSource={newData}
                     renderItem={item => (
                         <List.Item >
                             <Link to={`/profile/${item._id}`} style={{ width: '100%' }} onClick={handleCancel}>
@@ -54,3 +54,9 @@ export const CModalFollowing = connect(state => ({
     data: state?.promise?.findFollow?.payload?.following || [],
     status: state?.promise?.findFollow?.status
 }), { follow: actionFindFollowing })(ModalFolower)
+
+export const CModalPostLiked = connect(state => ({
+    data: state?.promise?.findLiked?.payload || [],
+    status: state?.promise?.findLiked?.status
+}), { follow: actionFindLiked })(ModalFolower)
+

+ 8 - 5
src/helpers/index.js

@@ -1,4 +1,5 @@
 import Icon from '@ant-design/icons';
+import { useEffect } from 'react';
 import { connect } from 'react-redux';
 import { Route } from 'react-router-dom';
 
@@ -43,12 +44,14 @@ const CircularGallerySvg = () =>
 
 export const CircularGalleryIcon = props => <Icon component={CircularGallerySvg} {...props} />
 
-const RRoute = ({action, component:Component,...routeProps}) => {
+const RRoute = ({ action, component: Component, ...routeProps }) => {
     const WrapperComponent = (componentProps) => {
-        action(componentProps.match)
-        return <Component {...componentProps}/>
+        useEffect(() => {
+            action(componentProps.match)
+        })
+        return <Component {...componentProps} />
     }
-    return <Route {...routeProps} component={WrapperComponent}/>
+    return <Route {...routeProps} component={WrapperComponent} />
 }
 
-export const CRRoute = connect(null, {action: match => ({type: 'ROUTE', match})})(RRoute)
+export const CRRoute = connect(null, { action: match => ({ type: 'ROUTE', match }) })(RRoute)

+ 1 - 1
src/pages/MainPostsFeed.jsx

@@ -15,7 +15,7 @@ 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} />
+            <UserAvatar avatar={owner?.avatar} avatarSize={'45px'} />
             <span className='nick'>{owner?.nick ? owner.nick : owner?.login ? owner.login : 'Null'}</span>
         </Link >
     </Row>

+ 26 - 33
src/pages/PostPage.jsx

@@ -11,12 +11,26 @@ import { createElement, useState } from 'react';
 import { Comment, Tooltip, Avatar } from 'antd';
 import moment from 'moment';
 import { DislikeOutlined, LikeOutlined, DislikeFilled, LikeFilled } from '@ant-design/icons';
+import { UserAvatar } from './Header';
+import { Link } from 'react-router-dom';
 
-const Demo = () => {
-    const [likes, setLikes] = useState(0);
+
+
+
+
+
+
+
+const PostPageTitle = ({ data: { owner } }) =>
+    <PostTitle owner={owner} />
+
+const CPostPageTitle = connect(state => ({ data: state?.postsFeed?.posts || {} }))(PostPageTitle)
+
+const PostComments = ({ comments: { _id, answerTo, answers, createdAt, likes, text, owner } }) => {
+    const [likejs, setLikes] = useState(0);
     const [dislikes, setDislikes] = useState(0);
     const [action, setAction] = useState(null);
-
+console.log(owner);
     const like = () => {
         setLikes(1);
         setDislikes(0);
@@ -36,25 +50,21 @@ const Demo = () => {
                 <span className="comment-action">{likes}</span>
             </span>
         </Tooltip>,
-        <Tooltip key="comment-basic-dislike" title="Dislike">
-            <span onClick={dislike}>
-                {createElement(action === 'disliked' ? DislikeFilled : DislikeOutlined)}
-                <span className="comment-action">{dislikes}</span>
-            </span>
-        </Tooltip>,
         <span key="comment-basic-reply-to">Reply to</span>,
     ];
-
+    const author = [
+        <Link to={`/profile/${owner?._id}`} >
+            <span className='nick'>{owner?.nick ? owner.nick : owner?.login ? owner.login : 'Null'}</span>
+        </Link>
+    ]
     return (
         <Comment
             actions={actions}
-            author={<a>Han Solo</a>}
-            avatar={<Avatar src="https://joeschmoe.io/api/v1/random" alt="Han Solo" />}
+            author={author}
+            avatar={<UserAvatar avatar={owner?.avatar} avatarSize={'35px'} />}
             content={
                 <p>
-                    We supply a series of design principles, practical patterns and high quality design
-                    resources (Sketch and Axure), to help people create their product prototypes beautifully
-                    and efficiently.
+                    {text}
                 </p>
             }
             datetime={
@@ -63,26 +73,10 @@ const Demo = () => {
                 </Tooltip>
             }
         />
-    );
-};
-
-
-
-
-
-const PostPageTitle = ({ data: { owner } }) =>
-    <PostTitle owner={owner} />
-
-const CPostPageTitle = connect(state => ({ data: state?.postsFeed?.posts || {} }))(PostPageTitle)
-
-const PostComments = ({ }) => {
-
-    return (
-        <>  </>
     )
 }
 
-const CPostComments = connect()(PostComments)
+const CPostComments = connect(state => ({ comments: state?.postsFeed?.posts?.coments || [] }))(PostComments)
 
 const PostPageDescrption = ({ data: { _id, likes, text, title, createdAt, } }) =>
     <div className='PostOne__description-inner'>
@@ -90,7 +84,6 @@ const PostPageDescrption = ({ data: { _id, likes, text, title, createdAt, } }) =
             <PostDescription title={title} description={text} date={createdAt} />
             <Divider plain><Text type='secodary'></Text>Comments</Divider>
             <CPostComments />
-            <Demo />
         </div>
         <div className='PostOne__description-bottom'>
             <Divider ></Divider>

File diff ditekan karena terlalu besar
+ 11 - 3
src/redux/saga/index.js


+ 32 - 2
yarn.lock

@@ -1049,7 +1049,7 @@
     core-js-pure "^3.19.0"
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
   integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
@@ -2422,6 +2422,11 @@ array-includes@^3.1.3, array-includes@^3.1.4:
     get-intrinsic "^1.1.1"
     is-string "^1.0.7"
 
+array-move@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/array-move/-/array-move-4.0.0.tgz#2c3730f056cc926f62a59769a5a8cda2fb6d8c55"
+  integrity sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==
+
 array-tree-filter@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190"
@@ -4859,6 +4864,13 @@ internal-slot@^1.0.3:
     has "^1.0.3"
     side-channel "^1.0.4"
 
+invariant@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+  integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+  dependencies:
+    loose-envify "^1.0.0"
+
 ip@^1.1.0:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -5855,7 +5867,7 @@ lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
-loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -7073,6 +7085,15 @@ prompts@^2.0.1, prompts@^2.4.2:
     kleur "^3.0.3"
     sisteransi "^1.0.5"
 
+prop-types@^15.5.7:
+  version "15.8.1"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+  integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.13.1"
+
 prop-types@^15.6.2, prop-types@^15.7.2:
   version "15.8.0"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.0.tgz#d237e624c45a9846e469f5f31117f970017ff588"
@@ -7663,6 +7684,15 @@ react-scripts@5.0.0:
   optionalDependencies:
     fsevents "^2.3.2"
 
+react-sortable-hoc@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7"
+  integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==
+  dependencies:
+    "@babel/runtime" "^7.2.0"
+    invariant "^2.2.4"
+    prop-types "^15.5.7"
+
 react@^17.0.2:
   version "17.0.2"
   resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"