Content of home page
This commit is contained in:
@@ -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 |
|
||||
|
||||
77
client/src/components/Friend.jsx
Normal file
77
client/src/components/Friend.jsx
Normal file
@@ -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 (
|
||||
<FlexBetween>
|
||||
<FlexBetween gap="1rem">
|
||||
<ProfilePhoto image={profilePicturePath} size="55px" />
|
||||
<Box
|
||||
onClick={() => {
|
||||
navigate(`/profile/${friendId}`);
|
||||
navigate(0);
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
color={main}
|
||||
variant="h5"
|
||||
fontWeight="500"
|
||||
sx={{
|
||||
"&:hover": { color: palette.primary.light, cursor: "pointer" },
|
||||
}}
|
||||
>
|
||||
{userName}
|
||||
</Typography>
|
||||
<Typography color={medium} fontSize="0.75rem">
|
||||
{subtitle}
|
||||
</Typography>
|
||||
</Box>
|
||||
</FlexBetween>
|
||||
<IconButton
|
||||
onClick={() => handleFriend()}
|
||||
sx={{ backgroundColor: primaryLight, p: "0.6rem" }}
|
||||
>
|
||||
{isFriend ? (
|
||||
<PersonRemoveOutlined sx={{ color: primaryDark }} />
|
||||
) : (
|
||||
<PersonAddOutlined sx={{ color: primaryDark }} />
|
||||
)}
|
||||
</IconButton>
|
||||
</FlexBetween>
|
||||
);
|
||||
};
|
||||
|
||||
export default Friend;
|
||||
@@ -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"}
|
||||
>
|
||||
<PostWidget profilePicturePath={profilePicturePath} />
|
||||
<FeedWidget userId={_id} />
|
||||
</Box>
|
||||
{isNotMobile && <Box flexBasis="26%"></Box>}
|
||||
</Box>
|
||||
|
||||
109
client/src/scenes/widgets/ContentWidget.jsx
Normal file
109
client/src/scenes/widgets/ContentWidget.jsx
Normal file
@@ -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 (
|
||||
<WidgetWrapper m="2rem 0">
|
||||
<Friend
|
||||
friendId={userId}
|
||||
userName={userName}
|
||||
subtitle={location}
|
||||
profilePicturePath={profilePicturePath}
|
||||
/>
|
||||
<Typography color={main} sx={{ mt: "1rem" }}>
|
||||
{content}
|
||||
</Typography>
|
||||
{contentPicturePath && (
|
||||
<img
|
||||
width="100%"
|
||||
height="auto"
|
||||
alt="post"
|
||||
style={{ borderRadius: "0.75rem", marginTop: "0.75rem" }}
|
||||
src={`http://localhost:3001/assets/${contentPicturePath}`}
|
||||
/>
|
||||
)}
|
||||
<FlexBetween mt="0.25rem">
|
||||
<FlexBetween gap="1rem">
|
||||
<FlexBetween gap="0.3rem">
|
||||
<IconButton onClick={handleLike}>
|
||||
{isLiked ? (
|
||||
<FavoriteOutlined sx={{ color: primary }} />
|
||||
) : (
|
||||
<FavoriteBorderOutlined />
|
||||
)}
|
||||
</IconButton>
|
||||
<Typography>{likesCount}</Typography>
|
||||
</FlexBetween>
|
||||
<FlexBetween gap="0.3rem">
|
||||
<IconButton onClick={() => setIsComments(!isComments)}>
|
||||
<ChatBubbleOutlineOutlined />
|
||||
</IconButton>
|
||||
<Typography>{comments.length}</Typography>
|
||||
</FlexBetween>
|
||||
</FlexBetween>
|
||||
<IconButton>
|
||||
<ShareOutlined />
|
||||
</IconButton>
|
||||
</FlexBetween>
|
||||
{isComments && (
|
||||
<Box mt="0.5rems">
|
||||
{comments.map((comment, i) => (
|
||||
<Box key={`${userName}-${i}`}>
|
||||
<Divider />
|
||||
<Typography sx={{ color: main, m: "0.5rem 0", pl: "1rem" }}>
|
||||
{comment}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
<Divider />
|
||||
</Box>
|
||||
)}
|
||||
</WidgetWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContentWidget;
|
||||
70
client/src/scenes/widgets/FeedWidget.jsx
Normal file
70
client/src/scenes/widgets/FeedWidget.jsx
Normal file
@@ -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,
|
||||
}) => (
|
||||
<ContentWidget
|
||||
key={_id}
|
||||
postId={_id}
|
||||
userId={userId}
|
||||
userName={`${firstName} ${lastName}`}
|
||||
location={location}
|
||||
content={content}
|
||||
profilePicturePath={profilePicturePath}
|
||||
contentPicturePath={contentPicturePath}
|
||||
likes={likes}
|
||||
comments={comments}
|
||||
></ContentWidget>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeedWidget;
|
||||
Reference in New Issue
Block a user