Skip to content

Commit

Permalink
post edit and post delete added
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandyrzph committed Aug 2, 2022
1 parent 74de40a commit 780071d
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 18 deletions.
2 changes: 1 addition & 1 deletion pixelplace/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function App() {
/>
<Route path="posts/:postId" element={<PostDetails />} />
<Route
path="post-edit/:postId"
path="posts/:postId/edit"
element={
<ProtectedRoute>
<PostEdit />
Expand Down
8 changes: 4 additions & 4 deletions pixelplace/src/components/Forms/PostCreateForm.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Formik, Form, Field, ErrorMessage } from "formik";
import { CreatePostSchema } from "../../utils/formValidators";
import { Formik, Form, Field } from "formik";
import { CreateEditPostSchema } from "../../utils/formValidators";
import { useEffect, useRef, useState } from "react";
import { AiOutlineCloudUpload } from "react-icons/ai";
import { useNavigate } from "react-router-dom";
import { db, storage } from "../../firebase";
import { addDoc, collection, doc, getDoc, serverTimestamp } from "firebase/firestore";
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { useUserAuth } from "../../context/UserAuthContext";
import { BsShieldFillExclamation } from "react-icons/bs";
Expand Down Expand Up @@ -68,7 +68,7 @@ const PostCreateForm = () => {
category: "",
image: null,
}}
validationSchema={CreatePostSchema}
validationSchema={CreateEditPostSchema}
onSubmit={(values) => handleSubmit(values)}
>
{({ touched, errors, setFieldValue }) => (
Expand Down
219 changes: 214 additions & 5 deletions pixelplace/src/components/Forms/PostEditForm.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,216 @@
import { Formik, Form, Field } from "formik";
import { CreateEditPostSchema } from "../../utils/formValidators";
import { useEffect, useRef, useState } from "react";
import { AiOutlineCloudUpload } from "react-icons/ai";
import { useNavigate, useParams } from "react-router-dom";
import { db, storage } from "../../firebase";
import { doc, updateDoc } from "firebase/firestore";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { useUserAuth } from "../../context/UserAuthContext";
import { BsShieldFillExclamation } from "react-icons/bs";
import { getPostById } from "../../api/PostsAPI";

const PostEditForm = () => {
return (
<div>PostEditForm</div>
)
}
const [image, setImage] = useState();
const [imageReceived, setImageReceived] = useState();
const [preview, setPreview] = useState();
const [post, setPost] = useState();
const navigate = useNavigate();
const { user } = useUserAuth();
const fileRef = useRef(null);
const { postId } = useParams();

useEffect(() => {
if (image) {
const reader = new FileReader();
reader.onloadend = () => {
setPreview(reader.result);
};
reader.readAsDataURL(image);
}
setPreview((prev) => null);
}, [image]);

useEffect(() => {
getPostById(postId)
.then((data) => {
setPost(data);
setImageReceived(data.image);
})
.catch((err) => console.log(err));
}, [postId]);

const uploadImage = async (image) => {
const imageRef = ref(storage, `${new Date().getTime() + image.name}`);
try {
const res = await uploadBytes(imageRef, image, {
contentType: "image/jpg" | "image/jpeg" | "image/svg" | "image/png",
});
return getDownloadURL(res.ref);
} catch (err) {
console.log(err);
}
};

const handleSubmit = async (values) => {
const url = await uploadImage(values.image);
try {
await updateDoc(doc(db, `Posts`, postId), {
...values,
image: url,
ownerAvatarURL:
user.photoURL ??
"https://firebasestorage.googleapis.com/v0/b/pixelplace-b8fac.appspot.com/o/1024px-Faenza-avatar-default-symbolic.svg.png?alt=media&token=986532b2-c109-4faf-b607-30ce2a1e1ff8",
});
navigate("/posts");
} catch (err) {
console.log(err);
}
};

if (post) {
return (
<Formik
initialValues={{
title: post?.title,
description: post?.description,
category: post?.category,
image: post?.image,
}}
validationSchema={CreateEditPostSchema}
onSubmit={(values) => handleSubmit(values)}
>
{({ values, touched, errors, setFieldValue }) => (
<Form className="w-full mx-auto h-screen pt-[100px] max-w-xs sm:max-w-sm md:max-w-lg lg:max-w-3xl">
<h1 className="relative text-[100px] text-5xl -z-10 -mb-[10px] font-logo">
Edit <span className="text-stroke text-white">Post</span>
</h1>
<div className="flex flex-wrap">
<Field
name="title"
className={`border-2 mt-3 w-full outline-none bg-white focus:shadow-[2px_2px_0px] duration-150 rounded-md ${
touched.title && errors.title
? "border-red-500 focus:shadow-[2px_2px_0px] focus:shadow-red-500 "
: "border-neu-black"
} px-2 py-2`}
placeholder="Enter post title"
/>
{touched.title && errors.title ? (
<p className="flex items-center gap-2 mt-1 text-red-600">
<BsShieldFillExclamation />
{errors.title}
</p>
) : null}

<Field
type="text"
name="description"
className={`border-2 mt-3 w-full outline-none bg-white focus:shadow-[2px_2px_0px] duration-150 rounded-md ${
touched.description && errors.description
? "border-red-500 focus:shadow-[2px_2px_0px] focus:shadow-red-500 "
: "border-neu-black"
} px-2 py-2`}
placeholder="Enter description"
/>
{touched.description && errors.description ? (
<p className="flex items-center gap-2 mt-1 text-red-600">
<BsShieldFillExclamation />
{errors.description}
</p>
) : null}
<Field
type="text"
name="category"
className={`border-2 mt-3 w-full outline-none bg-white focus:shadow-[2px_2px_0px] duration-150 rounded-md ${
touched.category && errors.category
? "border-red-500 focus:shadow-[2px_2px_0px] focus:shadow-red-500 "
: "border-neu-black"
} px-2 py-2`}
placeholder="Enter category"
/>
{touched.category && errors.category ? (
<p className="flex items-center gap-2 mt-1 text-red-600">
<BsShieldFillExclamation />
{errors.category}
</p>
) : null}

<div className="mt-3 flex justify-center md:flex-row flex-col items-center w-full">
<label
type="submit"
onClick={(e) => {
e.preventDefault();
fileRef.current.click();
}}
className={`relative z-[9] flex flex-col justify-center items-center w-full h-64 bg-white rounded-lg border-2 ${
touched.image && errors.image
? `border-red-600`
: "border-neu-black"
} border-dashed cursor-pointer`}
>
<div className="relative flex flex-col justify-center items-center pt-5 pb-6">
<AiOutlineCloudUpload size={"2rem"} />
<p className="mb-2 text-sm text-gray-500">
<span className="font-semibold">Click to upload</span>{" "}
or drag and drop
</p>
<p className="text-xs text-gray-500">Only valid images</p>
</div>
</label>
{(preview ?? imageReceived) && (
<div className="h-full">
<img
id="image"
src={preview ?? imageReceived}
alt="preview"
className="h-[250px] max-w-sm object-cover object-center p-2 z-10 hover:brightness-50 cursor-pointer duration-100"
onClick={(e) => {
setImage(null);
setImageReceived(null);
setFieldValue("image", null);
}}
/>
</div>
)}
</div>
<input
type="file"
name="image"
className="hidden"
ref={fileRef}
accept="image/*"
onChange={(e) => {
const file = e.target.files[0];
if (file && file.type.substring(0, 5) === "image") {
setImage(file);
setFieldValue("image", e.target.files[0]);
} else {
setImage(null);
setFieldValue("image", null);
}
}}
/>

{touched.image && errors.image ? (
<p className="flex items-center gap-2 mt-1 text-red-600">
<BsShieldFillExclamation />
{errors.image}
</p>
) : null}
<div className="mt-4 w-full">
<button
type="Submit"
className="relative flex-grow-1 w-full lg:w-full hover:shadow-neu-shadow hover:-translate-y-1 duration-200 border-2 outline-none border-neu-black px-6 py-3 rounded-lg bg-neu-yellow font-bold"
>
Submit
</button>
</div>
</div>
</Form>
)}
</Formik>
);
}
};

export default PostEditForm
export default PostEditForm;
69 changes: 65 additions & 4 deletions pixelplace/src/components/PostDetails/PostDetails.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { useEffect, useRef, useState } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import { BeatLoader } from "react-spinners";
import { getPostById } from "../../api/PostsAPI";
import Dots from "../../assets/26432.svg";
import { HiDotsHorizontal } from "react-icons/hi";
import { useUserAuth } from "../../context/UserAuthContext";
import { Transition } from "@tailwindui/react";
import { db } from "../../firebase";
import { deleteDoc, doc } from "firebase/firestore";

const PostDetails = () => {
const { user } = useUserAuth();
const { postId } = useParams();
const [post, setPost] = useState();
const [isLoading, setIsLoading] = useState(false);
const { postId } = useParams();
const [dropdownShow, setDropdownShow] = useState(false);
const menuRef = useRef(null);
const navigate = useNavigate();

useEffect(() => {
setIsLoading(true);
Expand All @@ -22,9 +28,29 @@ const PostDetails = () => {
.catch((err) => console.log(err));
}, [postId]);

useEffect(() => {
if (post) {
const handleOutsideClick = (event) => {
if (!menuRef.current.contains(event.target)) {
if (!dropdownShow) return;
setDropdownShow(false);
}
};

window.addEventListener("click", handleOutsideClick);
return () => window.removeEventListener("click", handleOutsideClick);
}
}, [post, dropdownShow, menuRef]);

const [comment, setComment] = useState("");
const [addingComment, setAddingComment] = useState(false);

const deletePostHandler = () => {
deleteDoc(doc(db, "Posts", postId)).then(() => {
navigate("/posts");
});
};

const addComment = () => {
if (comment) {
setAddingComment(true);
Expand All @@ -50,7 +76,42 @@ const PostDetails = () => {
<div className="relative border-2 border-t-none rounded-b-lg border-neu-black bg-white p-4">
<div className="flex justify-between">
<h1 className="text-xl font-neu">{post?.title}</h1>
<HiDotsHorizontal className="" />
{user && user.uid === post?.ownerId ? (
<div ref={menuRef} className="relative">
<HiDotsHorizontal
cursor={"pointer"}
onClick={() => setDropdownShow(!dropdownShow)}
/>
<Transition
show={dropdownShow}
enter="transition ease-out duration-100 transform"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="transition ease-in duration-75 transform"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div className="absolute z-[200] right-0 max-w-fit mt-1 bg-white rounded hover:shadow-[2px_2px_2px] border-2 border-neu-black duration-150">
<Link to="edit">
<p className="block px-4 py-2 hover:bg-neu-yellow hover:text-neu-black duration-150 truncate">
Edit Post
</p>
</Link>
<hr />
<button
onClick={() => {
deletePostHandler();
}}
className="text-left w-full"
>
<p className="block px-4 py-2 hover:bg-red-500 hover:text-neu-black duration-75">
Delete
</p>
</button>
</div>
</Transition>
</div>
) : null}
</div>
<div className="text-lg mb-2">{post?.description}</div>

Expand Down
Loading

0 comments on commit 780071d

Please sign in to comment.