Skip to content

Commit

Permalink
Fetch feed posts
Browse files Browse the repository at this point in the history
  • Loading branch information
burakorkmez committed Aug 18, 2023
1 parent e345b3c commit f71d886
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 12 deletions.
2 changes: 1 addition & 1 deletion backend/controllers/postController.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ const getFeedPosts = async (req, res) => {

const feedPosts = await Post.find({ postedBy: { $in: following } }).sort({ createdAt: -1 });

res.status(200).json({ feedPosts });
res.status(200).json(feedPosts);
} catch (err) {
res.status(500).json({ error: err.message });
}
Expand Down
17 changes: 15 additions & 2 deletions backend/controllers/userController.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ import User from "../models/userModel.js";
import bcrypt from "bcryptjs";
import generateTokenAndSetCookie from "../utils/helpers/generateTokenAndSetCookie.js";
import { v2 as cloudinary } from "cloudinary";
import mongoose from "mongoose";

const getUserProfile = async (req, res) => {
const { username } = req.params;
// We will fetch user profile either with username or userId
// query is either username or userId
const { query } = req.params;

try {
const user = await User.findOne({ username }).select("-password").select("-updatedAt");
let user;

// query is userId
if (mongoose.Types.ObjectId.isValid(query)) {
user = await User.findOne({ _id: query }).select("-password").select("-updatedAt");
} else {
// query is username
user = await User.findOne({ username: query }).select("-password").select("-updatedAt");
}

if (!user) return res.status(404).json({ error: "User not found" });

res.status(200).json(user);
Expand Down
2 changes: 1 addition & 1 deletion backend/routes/userRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import protectRoute from "../middlewares/protectRoute.js";

const router = express.Router();

router.get("/profile/:username", getUserProfile);
router.get("/profile/:query", getUserProfile);
router.post("/signup", signupUser);
router.post("/login", loginUser);
router.post("/logout", logoutUser);
Expand Down
24 changes: 24 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@chakra-ui/react": "^2.7.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"date-fns": "^2.30.0",
"framer-motion": "^10.12.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/LoginCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export default function LoginCard() {
</FormControl>
<Stack spacing={10} pt={2}>
<Button
loadingText='Submitting'
loadingText='Logging in'
size='lg'
bg={useColorModeValue("gray.600", "gray.700")}
color={"white"}
Expand Down
139 changes: 139 additions & 0 deletions frontend/src/components/Post.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { Avatar } from "@chakra-ui/avatar";
import { Image } from "@chakra-ui/image";
import { Box, Flex, Text } from "@chakra-ui/layout";
import { Link, useNavigate } from "react-router-dom";
import Actions from "./Actions";
import { useEffect, useState } from "react";
import useShowToast from "../hooks/useShowToast";
import { formatDistanceToNow } from "date-fns";

const Post = ({ post, postedBy }) => {
const [liked, setLiked] = useState(false);
const [user, setUser] = useState(null);
const showToast = useShowToast();

const navigate = useNavigate();

useEffect(() => {
const getUser = async () => {
try {
const res = await fetch("/api/users/profile/" + postedBy);
const data = await res.json();
console.log(data);
if (data.error) {
showToast("Error", data.error, "error");
return;
}
setUser(data);
} catch (error) {
showToast("Error", error.message, "error");
setUser(null);
}
};

getUser();
}, [postedBy, showToast]);

if (!user) return null;
return (
<Link to={`/${user.username}/post/${post._id}`}>
<Flex gap={3} mb={4} py={5}>
<Flex flexDirection={"column"} alignItems={"center"}>
<Avatar
size='md'
name={user.name}
src={user?.profilePic}
onClick={(e) => {
e.preventDefault();
navigate(`/${user.username}`);
}}
/>
<Box w='1px' h={"full"} bg='gray.light' my={2}></Box>
<Box position={"relative"} w={"full"}>
{post.replies.length === 0 && <Text textAlign={"center"}>🥱</Text>}
{post.replies[0] && (
<Avatar
size='xs'
name='John doe'
src={post.replies[0].userProfilePic}
position={"absolute"}
top={"0px"}
left='15px'
padding={"2px"}
/>
)}

{post.replies[1] && (
<Avatar
size='xs'
name='John doe'
src={post.replies[1].userProfilePic}
position={"absolute"}
bottom={"0px"}
right='-5px'
padding={"2px"}
/>
)}

{post.replies[2] && (
<Avatar
size='xs'
name='John doe'
src={post.replies[2].userProfilePic}
position={"absolute"}
bottom={"0px"}
left='4px'
padding={"2px"}
/>
)}
</Box>
</Flex>
<Flex flex={1} flexDirection={"column"} gap={2}>
<Flex justifyContent={"space-between"} w={"full"}>
<Flex w={"full"} alignItems={"center"}>
<Text
fontSize={"sm"}
fontWeight={"bold"}
onClick={(e) => {
e.preventDefault();
navigate(`/${user.username}`);
}}
>
{user?.username}
</Text>
<Image src='/verified.png' w={4} h={4} ml={1} />
</Flex>
<Flex gap={4} alignItems={"center"}>
<Text fontSize={"xs"} width={36} textAlign={"right"} color={"gray.light"}>
{formatDistanceToNow(new Date(post.createdAt))} ago
</Text>
</Flex>
</Flex>

<Text fontSize={"sm"}>{post.text}</Text>
{post.img && (
<Box borderRadius={6} overflow={"hidden"} border={"1px solid"} borderColor={"gray.light"}>
<Image src={post.img} w={"full"} />
</Box>
)}

<Flex gap={3} my={1}>
<Actions liked={liked} setLiked={setLiked} />
</Flex>

<Flex gap={2} alignItems={"center"}>
<Text color={"gray.light"} fontSize='sm'>
{post.replies.length} replies
</Text>
<Box w={0.5} h={0.5} borderRadius={"full"} bg={"gray.light"}></Box>
<Text color={"gray.light"} fontSize='sm'>
{post.likes.length} likes
</Text>
</Flex>
</Flex>
</Flex>
</Link>
);
};

export default Post;
48 changes: 41 additions & 7 deletions frontend/src/pages/HomePage.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
import { Button, Flex } from "@chakra-ui/react";
import { Link } from "react-router-dom";
import { Flex, Spinner } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import useShowToast from "../hooks/useShowToast";
import Post from "../components/Post";

const HomePage = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const showToast = useShowToast();
useEffect(() => {
const getFeedPosts = async () => {
setLoading(true);
try {
const res = await fetch("/api/posts/feed");
const data = await res.json();
if (data.error) {
showToast("Error", data.error, "error");
return;
}
console.log(data);
setPosts(data);
} catch (error) {
showToast("Error", error.message, "error");
} finally {
setLoading(false);
}
};
getFeedPosts();
}, [showToast]);

return (
<Link to={"/markzuckerberg"}>
<Flex w={"full"} justifyContent={"center"}>
<Button mx={"auto"}>Visit Profile Page</Button>
</Flex>
</Link>
<>
{!loading && posts.length === 0 && <h1>Follow some users to see the feed</h1>}

{loading && (
<Flex justify='center'>
<Spinner size='xl' />
</Flex>
)}

{posts.map((post) => (
<Post key={post._id} post={post} postedBy={post.postedBy} />
))}
</>
);
};

Expand Down

0 comments on commit f71d886

Please sign in to comment.