Skip to content

Commit

Permalink
Follow/Unfollow functionality added
Browse files Browse the repository at this point in the history
  • Loading branch information
burakorkmez committed Aug 15, 2023
1 parent 130bb63 commit fe65973
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 25 deletions.
98 changes: 84 additions & 14 deletions frontend/src/components/UserHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import { Avatar } from "@chakra-ui/avatar";
import { Box, Flex, Link, Text, VStack } from "@chakra-ui/layout";
import { Menu, MenuButton, MenuItem, MenuList } from "@chakra-ui/menu";
import { Portal } from "@chakra-ui/portal";
import { useToast } from "@chakra-ui/react";
import { Button, useToast } from "@chakra-ui/react";
import { BsInstagram } from "react-icons/bs";
import { CgMoreO } from "react-icons/cg";
import { useRecoilValue } from "recoil";
import userAtom from "../atoms/userAtom";
import { Link as RouterLink } from "react-router-dom";
import { useState } from "react";
import useShowToast from "../hooks/useShowToast";

const UserHeader = () => {
const UserHeader = ({ user }) => {
const toast = useToast();
const currentUser = useRecoilValue(userAtom); // logged in user
const [following, setFollowing] = useState(user.followers.includes(currentUser._id));
const showToast = useShowToast();
const [updating, setUpdating] = useState(false);

const copyURL = () => {
const currentURL = window.location.href;
Expand All @@ -22,36 +31,97 @@ const UserHeader = () => {
});
};

const handleFollowUnfollow = async () => {
if (!currentUser) {
showToast("Error", "Please login to follow", "error");
return;
}
if (updating) return;

setUpdating(true);
try {
const res = await fetch(`/api/users/follow/${user._id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
if (data.error) {
showToast("Error", data.error, "error");
return;
}

if (following) {
showToast("Success", `Unfollowed ${user.name}`, "success");
user.followers.pop(); // simulate removing from followers
} else {
showToast("Success", `Followed ${user.name}`, "success");
user.followers.push(currentUser._id); // simulate adding to followers
}
setFollowing(!following);

console.log(data);
} catch (error) {
showToast("Error", error, "error");
} finally {
setUpdating(false);
}
};

return (
<VStack gap={4} alignItems={"start"}>
<Flex justifyContent={"space-between"} w={"full"}>
<Box>
<Text fontSize={"2xl"} fontWeight={"bold"}>
Mark Zuckerberg
{user.name}
</Text>
<Flex gap={2} alignItems={"center"}>
<Text fontSize={"sm"}>markzuckerberg</Text>
<Text fontSize={"sm"}>{user.username}</Text>
<Text fontSize={"xs"} bg={"gray.dark"} color={"gray.light"} p={1} borderRadius={"full"}>
threads.net
</Text>
</Flex>
</Box>
<Box>
<Avatar
name='Mark Zuckerberg'
src='/zuck-avatar.png'
size={{
base: "md",
md: "xl",
}}
/>
{user.profilePic && (
<Avatar
name={user.name}
src={user.profilePic}
size={{
base: "md",
md: "xl",
}}
/>
)}
{!user.profilePic && (
<Avatar
name={user.name}
src='https://bit.ly/broken-link'
size={{
base: "md",
md: "xl",
}}
/>
)}
</Box>
</Flex>

<Text>Co-founder, executive chairman and CEO of Meta Platforms.</Text>
<Text>{user.bio}</Text>

{currentUser._id === user._id && (
<Link as={RouterLink} to='/update'>
<Button size={"sm"}>Update Profile</Button>
</Link>
)}
{currentUser._id !== user._id && (
<Button size={"sm"} onClick={handleFollowUnfollow} isLoading={updating}>
{following ? "Unfollow" : "Follow"}
</Button>
)}
<Flex w={"full"} justifyContent={"space-between"}>
<Flex gap={2} alignItems={"center"}>
<Text color={"gray.light"}>3.2K followers</Text>
<Text color={"gray.light"}>{user.followers.length} followers</Text>
<Box w='1' h='1' bg={"gray.light"} borderRadius={"full"}></Box>
<Link color={"gray.light"}>instagram.com</Link>
</Flex>
Expand Down
24 changes: 15 additions & 9 deletions frontend/src/hooks/useShowToast.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { useToast } from "@chakra-ui/toast";
import { useCallback } from "react";

const useShowToast = () => {
const toast = useToast();
const showToast = (title, description, status) => {
toast({
title,
description,
status,
duration: 3000,
isClosable: true,
});
};

const showToast = useCallback(
(title, description, status) => {
toast({
title,
description,
status,
duration: 3000,
isClosable: true,
});
},
[toast]
);

return showToast;
};

Expand Down
1 change: 1 addition & 0 deletions frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const colors = {
const theme = extendTheme({ config, styles, colors });

ReactDOM.createRoot(document.getElementById("root")).render(
// React.StrictMode renders every component twice (in the initial render), only in development.
<React.StrictMode>
<RecoilRoot>
<BrowserRouter>
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/pages/UpdateProfilePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ export default function UpdateProfilePage() {
password: "",
});
const fileRef = useRef(null);
const [updating, setUpdating] = useState(false);

const showToast = useShowToast();

const { handleImageChange, imgUrl } = usePreviewImg();

const handleSubmit = async (e) => {
e.preventDefault();

if (updating) return;
setUpdating(true);
try {
const res = await fetch(`/api/users/update/${user._id}`, {
method: "PUT",
Expand All @@ -51,6 +54,8 @@ export default function UpdateProfilePage() {
localStorage.setItem("user-threads", JSON.stringify(data));
} catch (error) {
showToast("Error", error, "error");
} finally {
setUpdating(false);
}
};
return (
Expand Down Expand Up @@ -150,6 +155,7 @@ export default function UpdateProfilePage() {
bg: "green.500",
}}
type='submit'
isLoading={updating}
>
Submit
</Button>
Expand Down
29 changes: 28 additions & 1 deletion frontend/src/pages/UserPage.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
import { useEffect, useState } from "react";
import UserHeader from "../components/UserHeader";
import UserPost from "../components/UserPost";
import { useParams } from "react-router-dom";
import useShowToast from "../hooks/useShowToast";

const UserPage = () => {
const [user, setUser] = useState(null);
const { username } = useParams();
const showToast = useShowToast();

useEffect(() => {
const getUser = async () => {
try {
const res = await fetch(`/api/users/profile/${username}`);
const data = await res.json();
if (data.error) {
showToast("Error", data.error, "error");
return;
}
setUser(data);
} catch (error) {
showToast("Error", error, "error");
}
};

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

if (!user) return null;

return (
<>
<UserHeader />
<UserHeader user={user} />
<UserPost likes={1200} replies={481} postImg='/post1.png' postTitle="Let's talk about threads." />
<UserPost likes={451} replies={12} postImg='/post2.png' postTitle='Nice tutorial. Highly recommended.' />
<UserPost
Expand Down

0 comments on commit fe65973

Please sign in to comment.