From 47381ee629c7cf032023fa5a49d7bda3b0a928bf Mon Sep 17 00:00:00 2001 From: Andrew Trieu Date: Fri, 21 Jul 2023 23:47:11 +0300 Subject: [PATCH] Content of home page --- README.md | 1 + client/src/components/Friend.jsx | 77 ++++++++++++++ client/src/scenes/homePage/index.jsx | 2 + client/src/scenes/widgets/ContentWidget.jsx | 109 ++++++++++++++++++++ client/src/scenes/widgets/FeedWidget.jsx | 70 +++++++++++++ 5 files changed, 259 insertions(+) create mode 100644 client/src/components/Friend.jsx create mode 100644 client/src/scenes/widgets/ContentWidget.jsx create mode 100644 client/src/scenes/widgets/FeedWidget.jsx diff --git a/README.md b/README.md index fb9afa2..d9de9ce 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,4 @@ Table: | 16.07.2023 | Fix mock schema and move mock injections out of index.js | 1 | | 18.07.2023 | Home page and navigate to login/register page if not logged in | 20 | | 19.07.2023 | Post widget | 10 | +| 21.07.2023 | Each post on home page | 20 | diff --git a/client/src/components/Friend.jsx b/client/src/components/Friend.jsx new file mode 100644 index 0000000..0d2086d --- /dev/null +++ b/client/src/components/Friend.jsx @@ -0,0 +1,77 @@ +import { PersonAddOutlined, PersonRemoveOutlined } from "@mui/icons-material"; +import { Box, IconButton, Typography, useTheme } from "@mui/material"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { setFriends } from "state"; +import FlexBetween from "./FlexBetween"; +import ProfilePhoto from "./ProfilePhoto"; + +const Friend = ({ friendId, userName, subtitle, profilePicturePath }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { _id } = useSelector((state) => state.user); + const token = useSelector((state) => state.token); + const friends = useSelector((state) => state.user.friends); + const { palette } = useTheme(); + const primaryLight = palette.primary.light; + const primaryDark = palette.primary.dark; + const main = palette.neutral.main; + const medium = palette.neutral.medium; + + const isFriend = friends.find((friend) => friend._id === friendId); + + const handleFriend = async () => { + const response = await fetch( + `http:/localhost:3001/users/${_id}/${friendId}`, + { + method: "PATCH", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + } + ); + const data = await response.json(); + dispatch(setFriends({ friends: data })); + }; + + return ( + + + + { + navigate(`/profile/${friendId}`); + navigate(0); + }} + > + + {userName} + + + {subtitle} + + + + handleFriend()} + sx={{ backgroundColor: primaryLight, p: "0.6rem" }} + > + {isFriend ? ( + + ) : ( + + )} + + + ); +}; + +export default Friend; diff --git a/client/src/scenes/homePage/index.jsx b/client/src/scenes/homePage/index.jsx index 3289eac..cb83261 100644 --- a/client/src/scenes/homePage/index.jsx +++ b/client/src/scenes/homePage/index.jsx @@ -3,6 +3,7 @@ import { useSelector } from "react-redux"; import Navbar from "scenes/navbar"; import UserWidget from "scenes/widgets/UserWidget"; import PostWidget from "scenes/widgets/PostWidget"; +import FeedWidget from "scenes/widgets/FeedWidget"; const HomePage = () => { const isNotMobile = useMediaQuery("(min-width: 1000px)"); @@ -26,6 +27,7 @@ const HomePage = () => { mt={isNotMobile ? undefined : "2rem"} > + {isNotMobile && } diff --git a/client/src/scenes/widgets/ContentWidget.jsx b/client/src/scenes/widgets/ContentWidget.jsx new file mode 100644 index 0000000..5bd95ad --- /dev/null +++ b/client/src/scenes/widgets/ContentWidget.jsx @@ -0,0 +1,109 @@ +import { + ChatBubbleOutlineOutlined, + FavoriteBorderOutlined, + FavoriteOutlined, + ShareOutlined, +} from "@mui/icons-material"; +import { Box, Divider, Typography, IconButton, useTheme } from "@mui/material"; +import FlexBetween from "components/FlexBetween"; +import Friend from "components/FriendList"; +import WidgetWrapper from "components/WidgetWrapper"; +import { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { setPosts } from "state"; + +const ContentWidget = ({ + postId, + userId, + userName, + location, + content, + profilePicturePath, + contentPicturePath, + likes, + comments, +}) => { + const [isComments, setIsComments] = useState(false); + const dispatch = useDispatch(); + const token = useSelector((state) => state.token); + const loggedInUserId = useSelector((state) => state.user._id); + const isLiked = Boolean(likes[loggedInUserId]); + const likesCount = Object.keys(likes).length; + const { palette } = useTheme(); + const primary = palette.primary.main; + const main = palette.neutral.main; + + const handleLike = async () => { + const response = await fetch(`http://localhost:3001/posts/${postId}/like`, { + method: "PATCH", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ userId: loggedInUserId }), + }); + const updatedPost = await response.json(); + dispatch(setPosts({ post: updatedPost })); + }; + + return ( + + + + {content} + + {contentPicturePath && ( + post + )} + + + + + {isLiked ? ( + + ) : ( + + )} + + {likesCount} + + + setIsComments(!isComments)}> + + + {comments.length} + + + + + + + {isComments && ( + + {comments.map((comment, i) => ( + + + + {comment} + + + ))} + + + )} + + ); +}; + +export default ContentWidget; diff --git a/client/src/scenes/widgets/FeedWidget.jsx b/client/src/scenes/widgets/FeedWidget.jsx new file mode 100644 index 0000000..2aba9c0 --- /dev/null +++ b/client/src/scenes/widgets/FeedWidget.jsx @@ -0,0 +1,70 @@ +import { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { setPosts } from "state"; +import ContentWidget from "./ContentWidget"; + +const FeedWidget = ({ userId, isProfile = false }) => { + const dispatch = useDispatch(); + const posts = useSelector((state) => state.posts); + const token = useSelector((state) => state.token); + + const getPosts = async () => { + const response = await fetch(`http://localhost:3001/posts`, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const posts = await response.json(); + dispatch(setPosts({ posts })); + }; + + const getUserPosts = async () => { + const response = await fetch(`http://localhost:3001/posts/${userId}`, { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const posts = await response.json(); + dispatch(setPosts({ posts })); + }; + + useEffect(() => { + isProfile ? getUserPosts() : getPosts(); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + return ( + <> + {posts.map( + ({ + _id, + userId, + firstName, + lastName, + location, + content, + profilePicturePath, + contentPicturePath, + likes, + comments, + }) => ( + + ) + )} + + ); +}; + +export default FeedWidget;