From fba2ab4f9be16b141df07c84223a7871b5243ea4 Mon Sep 17 00:00:00 2001 From: pratyushsingha Date: Tue, 5 Mar 2024 01:37:47 +0530 Subject: [PATCH] feat: tweet create feature added --- package.json | 2 + src/components/CommentCard.jsx | 95 ++++++++++++++++- src/components/Index.jsx | 6 ++ src/components/TweetBox.jsx | 189 +++++++++++++++++++++++++++++++++ src/components/ui/switch.jsx | 24 +++++ src/components/ui/textarea.jsx | 18 ++++ src/context/AppContext.jsx | 24 ++++- src/index.css | 2 +- src/pages/Home.jsx | 3 +- yarn.lock | 33 ++++++ 10 files changed, 390 insertions(+), 6 deletions(-) create mode 100644 src/components/TweetBox.jsx create mode 100644 src/components/ui/switch.jsx create mode 100644 src/components/ui/textarea.jsx diff --git a/package.json b/package.json index bfbfd56..4e86173 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,13 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toast": "^1.1.5", "axios": "^1.6.7", "check-password-strength": "^2.0.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "emoji-picker-react": "^4.8.0", "lucide-react": "^0.340.0", "moment": "^2.30.1", "react": "^18.2.0", diff --git a/src/components/CommentCard.jsx b/src/components/CommentCard.jsx index 1400736..29ddacf 100644 --- a/src/components/CommentCard.jsx +++ b/src/components/CommentCard.jsx @@ -87,12 +87,101 @@ const CommentCard = ({ > see replies - {replies.map((reply) => ( -

{reply.content}

- ))} + {replies.map( + ({ + _id, + ownerDetails, + updatedAt, + content, + images, + isLiked, + likeCount, + }) => ( +
+
+
+
+ {ownerDetails?.username} +
+
+
+
+
+

+ {ownerDetails?.username} +

+ + {moment(updatedAt, "YYYYMMDD").fromNow()} + +
+ +
+

{content}

+ {images && ( + <> +
+ {images.map((image, index) => ( + {image} + ))} +
+ + )} +
+ + + +
+
+
+
+ ) + )} ); }; diff --git a/src/components/Index.jsx b/src/components/Index.jsx index 11e2312..9fd7a98 100644 --- a/src/components/Index.jsx +++ b/src/components/Index.jsx @@ -16,6 +16,8 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Separator } from "./ui/separator"; +import { Switch } from "./ui/switch"; import Login from "@/pages/auth/Login.jsx"; import App from "@/App"; @@ -27,6 +29,7 @@ import Spinner from "@/components/loader/Spinner"; import Container from "@/components/Container"; import Sidebar from "@/components/Sidebar"; import CommentCard from "@/components/CommentCard"; +import TweetBox from "@/components/TweetBox"; export { sidebarItems, @@ -55,4 +58,7 @@ export { DialogTitle, DialogTrigger, CommentCard, + TweetBox, + Separator, + Switch, }; diff --git a/src/components/TweetBox.jsx b/src/components/TweetBox.jsx new file mode 100644 index 0000000..d4a95d6 --- /dev/null +++ b/src/components/TweetBox.jsx @@ -0,0 +1,189 @@ +import { useContext, useEffect, useState } from "react"; +import { + AppContext, + Button, + Label, + Separator, + useToast, + Switch, +} from "./Index"; +import { Hash, Image, SmilePlus } from "lucide-react"; +import EmojiPicker from "emoji-picker-react"; +import axios from "axios"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; + +const TweetBox = () => { + const { toast } = useToast(); + const { userDetails, setLoading } = useContext(AppContext); + const [openImoji, setOpenImoji] = useState(false); + const [isAnonymous, setIsAnonymous] = useState(false); + const [tagClick, setTagClick] = useState(false); + const [previewImages, setPreviewImages] = useState([]); + const [multipleImages, setMultipleImages] = useState(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting, isDirty, isTouched, isSubmitSuccessful }, + reset, + } = useForm({ + defaultValues: { + content: "", + tags: "", + images: [], + }, + }); + + const handleMultipleImages = (e) => { + if (e.target.files) { + setMultipleImages(e.target.files); + const imgArr = Array.from(e.target.files).map((file) => + URL.createObjectURL(file) + ); + setPreviewImages((prevImg) => prevImg.concat(imgArr)); + } + }; + + const toggleAnonymous = (value) => { + setIsAnonymous(value); + if (value === true) { + toast({ + title: "Tweet set to anonymous", + status: "success", + }); + } else { + toast({ + title: "Tweet set to public", + status: "success", + }); + } + }; + + const createTweet = async (data) => { + setLoading(true); + try { + const formdata = new FormData(); + formdata.append("content", data.content); + formdata.append("tags", data.tags); + formdata.append("isAnonymous", isAnonymous); + for (let i = 0; i < data.images.length; i++) { + formdata.append("images", data.images[i]); + } + const response = await axios.post( + `${import.meta.env.VITE_BACKEND_URL}/tweet`, + formdata, + { + withCredentials: true, + headers: { + "Content-Type": "multipart/form-data", + }, + } + ); + console.log(data); + toast({ + title: response.data.message, + }); + setLoading(false); + console.log(response); + } catch (error) { + console.log(error); + setLoading(false); + } + }; + + const render = (data) => { + return ( +
+ {data.map((image, index) => ( + {image} + ))} +
+ ); + }; + + useEffect(() => { + if (isSubmitSuccessful) + reset({ + content: "", + tags: "", + images: [], + }); + }, [isSubmitSuccessful]); + return ( +
+
+
+ +
+