From e17084dfff9be5f0a1877889fbd3f163b7d5ea5b Mon Sep 17 00:00:00 2001 From: pratyushsingha Date: Wed, 13 Mar 2024 01:19:31 +0530 Subject: [PATCH] feat: Implemented user liked and bookmarked tweets functionality in user profile --- src/components/Index.jsx | 4 ++ src/components/Sidebar.jsx | 18 ++++--- src/components/TweetCard.jsx | 89 ++++++++++++++++++++++++++++++---- src/main.jsx | 6 +++ src/pages/BookmarkedTweets.jsx | 60 +++++++++++++++++++++++ src/pages/Home.jsx | 57 +--------------------- src/pages/LikedTweets.jsx | 58 ++++++++++++++++++++++ src/pages/TweetDetails.jsx | 47 +----------------- src/pages/Tweets.jsx | 43 ++++++++++++++++ src/utils/index.jsx | 11 ++--- 10 files changed, 268 insertions(+), 125 deletions(-) create mode 100644 src/pages/BookmarkedTweets.jsx create mode 100644 src/pages/LikedTweets.jsx create mode 100644 src/pages/Tweets.jsx diff --git a/src/components/Index.jsx b/src/components/Index.jsx index 1815b4e..6f91591 100644 --- a/src/components/Index.jsx +++ b/src/components/Index.jsx @@ -43,6 +43,8 @@ import Profile from "@/pages/auth/Profile"; import UserDetails from "@/pages/auth/UserDetails"; import InputDiv from "./InputDiv"; import PassStrengthBar from "./PaawordStrengthBar"; +import Tweets from "@/pages/Tweets"; +import BookmarkedTweets from "@/pages/BookmarkedTweets"; export { sidebarItems, @@ -86,4 +88,6 @@ export { CardTitle, InputDiv, PassStrengthBar, + Tweets, + BookmarkedTweets, }; diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 36f70cc..17a7c94 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,17 +1,19 @@ +import { Link } from "react-router-dom"; import { sidebarItems, Button } from "./Index"; const Sidebar = () => { return (
{sidebarItems.map((item) => ( - + + + ))}
diff --git a/src/components/TweetCard.jsx b/src/components/TweetCard.jsx index b335a5c..5c05098 100644 --- a/src/components/TweetCard.jsx +++ b/src/components/TweetCard.jsx @@ -2,6 +2,7 @@ import { useContext, useRef } from "react"; import { Link } from "react-router-dom"; import moment from "moment"; import { Heart, Bookmark, Share2, MessageCircle, Copy } from "lucide-react"; +import axios from "axios"; import { Label, @@ -16,22 +17,87 @@ import { DialogTrigger, Button, useToast, + AppContext, } from "@/components/Index"; -const TweetCard = ({ tweet, bookMarkTweet, toggleLike }) => { +const TweetCard = ({ + tweet, + setTweets, + removeFromBookmarks, + removeFromLikes, +}) => { const { toast } = useToast(); const inputRef = useRef(); const handleCopy = () => { inputRef.current?.select(); window.navigator.clipboard.writeText( - `${window.location.href}tweet/${tweet._id}` + `${import.meta.env.VITE_FRONTEND_URL}/tweet/${tweet._id}` ); toast({ title: "Copied to clipboard", }); }; + const toggleLike = async (tweetId, e) => { + e.preventDefault(); + try { + const response = await axios.post( + `${import.meta.env.VITE_BACKEND_URL}/like/tweet/${tweetId}`, + {}, + { withCredentials: true } + ); + const updatedTweet = response.data.data[0]; + setTweets((prevTweets) => + prevTweets.map((tweet) => + tweet._id === updatedTweet._id ? updatedTweet : tweet + ) + ); + + if (typeof removeFromLikes === "function" && !updatedTweet.isLiked) { + removeFromLikes(tweetId); + } + } catch (error) { + console.log(error); + toast({ + variant: "destructive", + title: "error", + description: `${error.message}`, + }); + } + }; + + const bookMarkTweet = async (tweetId, e) => { + e.preventDefault(); + try { + const response = await axios.post( + `${import.meta.env.VITE_BACKEND_URL}/bookmarks/${tweetId}`, + {}, + { withCredentials: true } + ); + + const updatedTweet = response.data.data[0]; + setTweets((prevTweets) => + prevTweets.map((tweet) => + tweet._id === updatedTweet._id ? updatedTweet : tweet + ) + ); + if ( + typeof removeFromBookmarks === "function" && + !updatedTweet.isBookmarked + ) { + removeFromBookmarks(tweetId); + } + } catch (error) { + console.log(error); + toast({ + variant: "destructive", + title: "error", + description: `${error.message}`, + }); + } + }; + return (
@@ -42,18 +108,21 @@ const TweetCard = ({ tweet, bookMarkTweet, toggleLike }) => { ? "https://i.postimg.cc/Pxd7wvnh/image.png" : tweet?.ownerDetails?.avatar } - alt={ - tweet.isAnonymous ? "Anonymous" : tweet?.ownerDetails?.username - } + alt={tweet.isAnonymous ? "Anonymous" : tweet.ownerDetails?.username} className="h-full w-full rounded-full object-cover" />
-

- {tweet.isAnonymous ? "Anonymous" : tweet.ownerDetails.username} -

+ {tweet.ownerDetails && ( +

+ {tweet.isAnonymous + ? "Anonymous" + : tweet.ownerDetails.username} +

+ )} + {moment(tweet.updatedAt, "YYYYMMDD").fromNow()} @@ -123,7 +192,9 @@ const TweetCard = ({ tweet, bookMarkTweet, toggleLike }) => { diff --git a/src/main.jsx b/src/main.jsx index 9b6fe12..c61ad49 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -11,8 +11,11 @@ import { TweetDetails, Profile, UserDetails, + Tweets, + BookmarkedTweets } from "@/components/Index"; import "./index.css"; +import LikedTweets from "./pages/LikedTweets"; const router = createBrowserRouter([ { @@ -25,6 +28,9 @@ const router = createBrowserRouter([ path: "/profile/:username", element: , children: [ + { path: "/profile/:username/", element: }, + { path: "/profile/:username/bookmarkedTweets", element: }, + { path: "/profile/:username/likedTweets", element: }, { path: "/profile/:username/edit", element: }, ], }, diff --git a/src/pages/BookmarkedTweets.jsx b/src/pages/BookmarkedTweets.jsx new file mode 100644 index 0000000..a9afd3f --- /dev/null +++ b/src/pages/BookmarkedTweets.jsx @@ -0,0 +1,60 @@ +import { useEffect, useState } from "react"; +import { Spinner, TweetCard, useToast } from "@/components/Index"; +import axios from "axios"; + +const BookmarkedTweets = () => { + const { toast } = useToast(); + const [loading, setLoading] = useState(false); + const [tweets, setTweets] = useState([]); + + const bookmarkedTweets = async () => { + setLoading(true); + try { + const response = await axios.get( + `${import.meta.env.VITE_BACKEND_URL}/bookmarks`, + { + withCredentials: true, + } + ); + // console.log(response.data.data[0].bookmarkedTweet) + setTweets(response.data.data[0].bookmarkedTweet); + setLoading(false); + } catch (error) { + console.log(error); + toast({ + variant: "destructive", + title: "error", + description: `${error.message}`, + }); + setLoading(false); + } + }; + + const removeFromBookmarks = (tweetId) => { + setTweets((prevTweets) => + prevTweets.filter((tweet) => tweet._id !== tweetId) + ); + toast({ + title: "Removed from your bookmarks", + }); + }; + + useEffect(() => { + bookmarkedTweets(); + }, []); + + return loading ? ( + + ) : ( + tweets.map((tweet) => ( + + )) + ); +}; + +export default BookmarkedTweets; diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index add26ef..d89d1ca 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -34,56 +34,6 @@ const Home = () => { } }; - const toggleLike = async (tweetId, e) => { - e.preventDefault(); - try { - const response = await axios.post( - `${import.meta.env.VITE_BACKEND_URL}/like/tweet/${tweetId}`, - {}, - { withCredentials: true } - ); - const updatedTweet = response.data.data[0]; - setTweets((prevTweets) => - prevTweets.map((tweet) => - tweet._id === updatedTweet._id ? updatedTweet : tweet - ) - ); - } catch (error) { - console.log(error); - toast({ - variant: "destructive", - title: "error", - description: `${error.message}`, - }); - } - }; - - const bookMarkTweet = async (tweetId, e) => { - e.preventDefault(); - try { - const response = await axios.post( - `${import.meta.env.VITE_BACKEND_URL}/bookmarks/${tweetId}`, - {}, - { withCredentials: true } - ); - // console.log(response.data.data[0]); - - const updatedTweet = response.data.data[0]; - setTweets((prevTweets) => - prevTweets.map((tweet) => - tweet._id === updatedTweet._id ? updatedTweet : tweet - ) - ); - } catch (error) { - console.log(error); - toast({ - variant: "destructive", - title: "error", - description: `${error.message}`, - }); - } - }; - useEffect(() => { getFeedTweets(); }, [page]); @@ -97,12 +47,7 @@ const Home = () => { <> {tweets.map((tweet, index) => ( - + ))} ); diff --git a/src/pages/LikedTweets.jsx b/src/pages/LikedTweets.jsx new file mode 100644 index 0000000..46d6968 --- /dev/null +++ b/src/pages/LikedTweets.jsx @@ -0,0 +1,58 @@ +import { Spinner, TweetCard, useToast } from "@/components/Index"; +import axios from "axios"; +import React, { useEffect, useState } from "react"; + +const LikedTweets = () => { + const { toast } = useToast(); + const [loading, setLoading] = useState(false); + const [tweets, setTweets] = useState([]); + + const userTweets = async () => { + setLoading(true); + try { + const response = await axios.get( + `${import.meta.env.VITE_BACKEND_URL}/like/tweets`, + { + withCredentials: true, + } + ); + console.log(response); + setTweets(response.data.data[0].likedTweets); + setLoading(false); + } catch (error) { + console.log(error); + toast({ + variant: "destructive", + title: "error", + description: `${error.message}`, + }); + setLoading(false); + } + }; + + const removeFromLikes = (tweetId) => { + setTweets((prevTweets) => + prevTweets.filter((tweet) => tweet._id !== tweetId) + ); + toast({ + title: "Removed from your likes", + }); + }; + + useEffect(() => { + userTweets(); + }, []); + + loading && ; + + return tweets.map((tweet) => ( + + )); +}; + +export default LikedTweets; diff --git a/src/pages/TweetDetails.jsx b/src/pages/TweetDetails.jsx index 2055b3d..08aa678 100644 --- a/src/pages/TweetDetails.jsx +++ b/src/pages/TweetDetails.jsx @@ -34,46 +34,6 @@ const TweetDetails = () => { } }, [id, setProgress]); - const toggleLike = async (tweetId, e) => { - e.preventDefault(); - try { - const response = await axios.post( - `${import.meta.env.VITE_BACKEND_URL}/like/tweet/${tweetId}`, - {}, - { withCredentials: true } - ); - const updatedTweet = response.data.data; - setTweet(updatedTweet); - } catch (error) { - console.log(error); - toast({ - variant: "destructive", - title: "error", - description: `${error.message}`, - }); - } - }; - - const bookMarkTweet = async (tweetId, e) => { - e.preventDefault(); - try { - const response = await axios.post( - `${import.meta.env.VITE_BACKEND_URL}/bookmarks/${tweetId}`, - {}, - { withCredentials: true } - ); - const updatedTweet = response.data.data; - setTweet(updatedTweet); - } catch (error) { - console.log(error); - toast({ - variant: "destructive", - title: "error", - description: `${error.message}`, - }); - } - }; - const toggleCommentLike = async (commentId, e) => { e.preventDefault(); try { @@ -149,12 +109,7 @@ const TweetDetails = () => { return ( <> {tweet.map((t) => ( - + ))} {comments.map((comment) => ( { + const { toast } = useToast(); + const [loading, setLoading] = useState(false); + const [tweets, setTweets] = useState([]); + + const userTweets = async () => { + setLoading(true); + try { + const response = await axios.get( + `${import.meta.env.VITE_BACKEND_URL}/tweet/my`, + { + withCredentials: true, + } + ); + setTweets(response.data.data); + setLoading(false); + } catch (error) { + console.log(error); + toast({ + variant: "destructive", + title: "error", + description: `${error.message}`, + }); + setLoading(false); + } + }; + + useEffect(() => { + userTweets(); + }, []); + + loading && ; + + return tweets.map((tweet) => ( + + )); +}; + +export default Tweets; diff --git a/src/utils/index.jsx b/src/utils/index.jsx index 932b4b2..fa86b3e 100644 --- a/src/utils/index.jsx +++ b/src/utils/index.jsx @@ -1,30 +1,29 @@ -import { Home, BellDot, Mails, Bot, BookmarkCheck } from "lucide-react"; +import { Home, BellDot, Mails, Bot } from "lucide-react"; export const sidebarItems = [ { _id: 1, title: "Home", icon: , + path: "", }, { _id: 2, title: "Notifications", icon: , + path: "notifications", }, { _id: 3, title: "Messages", icon: , + path: "chat", }, { _id: 4, title: "Grok", icon: , - }, - { - _id: 5, - title: "Bookmarks", - icon: , + path: "grok", }, ];