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 |
|
| 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 |
|
| 18.07.2023 | Home page and navigate to login/register page if not logged in | 20 |
|
||||||
| 19.07.2023 | Post widget | 10 |
|
| 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 Navbar from "scenes/navbar";
|
||||||
import UserWidget from "scenes/widgets/UserWidget";
|
import UserWidget from "scenes/widgets/UserWidget";
|
||||||
import PostWidget from "scenes/widgets/PostWidget";
|
import PostWidget from "scenes/widgets/PostWidget";
|
||||||
|
import FeedWidget from "scenes/widgets/FeedWidget";
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const isNotMobile = useMediaQuery("(min-width: 1000px)");
|
const isNotMobile = useMediaQuery("(min-width: 1000px)");
|
||||||
@@ -26,6 +27,7 @@ const HomePage = () => {
|
|||||||
mt={isNotMobile ? undefined : "2rem"}
|
mt={isNotMobile ? undefined : "2rem"}
|
||||||
>
|
>
|
||||||
<PostWidget profilePicturePath={profilePicturePath} />
|
<PostWidget profilePicturePath={profilePicturePath} />
|
||||||
|
<FeedWidget userId={_id} />
|
||||||
</Box>
|
</Box>
|
||||||
{isNotMobile && <Box flexBasis="26%"></Box>}
|
{isNotMobile && <Box flexBasis="26%"></Box>}
|
||||||
</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