0% found this document useful (0 votes)
4 views9 pages

Post Management and Commenting System

The document is a React component for a PostPage that allows users to view, like, edit, and delete posts, as well as add, edit, and delete comments. It utilizes hooks for managing posts and comments, handles user authentication, and provides image upload functionality. The component also includes loading and error handling, ensuring a smooth user experience.

Uploaded by

bc0pants
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views9 pages

Post Management and Commenting System

The document is a React component for a PostPage that allows users to view, like, edit, and delete posts, as well as add, edit, and delete comments. It utilizes hooks for managing posts and comments, handles user authentication, and provides image upload functionality. The component also includes loading and error handling, ensuring a smooth user experience.

Uploaded by

bc0pants
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

import React, { useState, useEffect } from "react";

import { useParams, Link, useNavigate } from "react-router-dom";


import { usePosts } from "../hooks/usePosts";
import { useComments } from "../hooks/useComments";
import { useAppwriteUpload } from "../hooks/useAppwriteUpload";

const PostPage = () => {


const { postId, flagId } = useParams();
const {
post,
error: postError,
isLoading: postLoading,
getPostById,
likePost,
deletePost,
updatePost,
} = usePosts();
const {
error: commentError,
isLoading: commentLoading,
createComment,
deleteComment,
editComment,
} = useComments();
const { uploadImage, isUploading, uploadError } = useAppwriteUpload();
const [commentContent, setCommentContent] = useState("");
const [comments, setComments] = useState([]);
const [editingComment, setEditingComment] = useState(null);
const [editContent, setEditContent] = useState("");
const [currentUserId, setCurrentUserId] = useState(null);
const [isEditing, setIsEditing] = useState(false);
const [editForm, setEditForm] = useState({
title: "",
body: "",
picture: "",
});
const [selectedFile, setSelectedFile] = useState(null);
const [imagePreview, setImagePreview] = useState(null);
const navigate = useNavigate();

useEffect(() => {
getPostById(postId);
}, [postId]);

useEffect(() => {
if (post) {
setComments([Link] || []);
setEditForm({
title: [Link],
body: [Link],
picture: [Link] || "",
});
}
}, [post]);

useEffect(() => {
// Get current user ID from token
const token = [Link]("token");
if (token) {
try {
const payload = [Link](atob([Link](".")[1]));
setCurrentUserId([Link]);
} catch (err) {
[Link]("Error parsing token:", err);
}
}
}, []);

const handleLike = async () => {


const token = [Link]("token");
if (!token) {
navigate("/login");
return;
}

try {
const success = await likePost(postId);
if (success) {
// The post state will be updated by the hook
}
} catch (err) {
[Link]("Error liking post:", err);
}
};

const handleDelete = async () => {


if (![Link]("Are you sure you want to delete this post?")) {
return;
}

try {
const success = await deletePost(postId);
if (success) {
navigate(`/category/${flagId}`);
}
} catch (err) {
[Link]("Error deleting post:", err);
}
};

const handleEdit = () => {


setIsEditing(true);
};

const handleEditSubmit = async (e) => {


[Link]();
try {
let pictureUrl = [Link];
if (selectedFile) {
pictureUrl = await uploadImage(selectedFile);
if (!pictureUrl) throw new Error(uploadError || "Image upload failed");
}

const success = await updatePost(postId, {


...editForm,
picture: pictureUrl,
});
if (success) {
setIsEditing(false);
setSelectedFile(null);
setImagePreview(null);
}
} catch (err) {
[Link]("Error updating post:", err);
}
};

const handleEditChange = (e) => {


const { name, value } = [Link];
setEditForm((prev) => ({
...prev,
[name]: value,
}));
};

const handleImageChange = (e) => {


const file = [Link][0];
if (!file) return;

if (![Link]("image/")) {
alert("Please select an image file");
return;
}

if ([Link] > 5 * 1024 * 1024) {


alert("Image size should be less than 5MB");
return;
}

const reader = new FileReader();


[Link] = () => setImagePreview([Link]);
[Link](file);

setSelectedFile(file);
};

const handleCommentSubmit = async (e) => {


[Link]();
const token = [Link]("token");
if (!token) {
navigate("/login");
return;
}

if (![Link]()) return;

try {
const newComment = await createComment(postId, commentContent);
if (newComment) {
setComments((prev) => [newComment, ...prev]);
setCommentContent("");
}
} catch (err) {
[Link]("Error posting comment:", err);
}
};
const handleDeleteComment = async (commentId) => {
try {
const success = await deleteComment(commentId);
if (success) {
setComments((prev) =>
[Link]((comment) => comment._id !== commentId)
);
}
} catch (err) {
[Link]("Error deleting comment:", err);
}
};

const handleEditComment = async (commentId) => {


try {
const updatedComment = await editComment(commentId, editContent);
if (updatedComment) {
setComments((prev) =>
[Link]((comment) =>
comment._id === commentId ? updatedComment : comment
)
);
setEditingComment(null);
setEditContent("");
}
} catch (err) {
[Link]("Error editing comment:", err);
}
};

const startEditing = (comment) => {


setEditingComment(comment._id);
setEditContent([Link]);
};

const cancelEditing = () => {


setEditingComment(null);
setEditContent("");
};

const isLoading = postLoading || commentLoading;


const error = postError || commentError;

if (isLoading) {
return (
<div className="min-h-screen flex justify-center items-center bg-[#1e1e2e]
pt-16">
<div className="text-[#89b4fa] text-xl">Loading...</div>
</div>
);
}

if (error) {
return (
<div className="min-h-screen flex justify-center items-center bg-[#1e1e2e]
pt-16">
<div className="text-red-500 text-xl">{error}</div>
</div>
);
}

if (!post) {
return (
<div className="min-h-screen flex justify-center items-center bg-[#1e1e2e]
pt-16">
<div className="text-[#89b4fa] text-xl">Post not found</div>
</div>
);
}

const isLiked = [Link]?.includes(currentUserId);


const isAuthor = currentUserId === [Link]?._id;

return (
<div className="min-h-screen bg-[#1e1e2e] pt-16">
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto">
<div className="mb-8">
<Link
to={`/category/${flagId}`}
className="text-[#89b4fa] hover:text-[#74c7ec] mb-4 inline-block"
>
← Back to Category
</Link>
{isEditing ? (
<form onSubmit={handleEditSubmit} className="space-y-4">
<input
type="text"
name="title"
value={[Link]}
onChange={handleEditChange}
className="w-full p-2 rounded bg-[#313244] text-[#cdd6f4] border
border-[#45475a] focus:outline-none focus:border-[#89b4fa]"
required
/>
<textarea
name="body"
value={[Link]}
onChange={handleEditChange}
className="w-full p-2 rounded bg-[#313244] text-[#cdd6f4] border
border-[#45475a] focus:outline-none focus:border-[#89b4fa]"
rows="6"
required
/>
<div>
<label className="block text-[#a6adc8] mb-2">
Image (optional)
</label>
<input
type="file"
accept="image/*"
onChange={handleImageChange}
className="w-full p-2 rounded bg-[#313244] text-[#cdd6f4]
border border-[#45475a] focus:outline-none focus:border-[#89b4fa]"
/>
{imagePreview && (
<div className="mt-2">
<img
src={imagePreview}
alt="Preview"
className="max-w-full h-auto rounded"
/>
</div>
)}
</div>
<div className="flex justify-end space-x-4">
<button
type="button"
onClick={() => setIsEditing(false)}
className="px-4 py-2 text-[#a6adc8] hover:text-[#89b4fa]
transition-colors duration-300"
>
Cancel
</button>
<button
type="submit"
className="px-4 py-2 bg-[#89b4fa] text-[#1e1e2e] rounded
hover:bg-[#74c7ec] transition-colors duration-300"
>
Save Changes
</button>
</div>
</form>
) : (
<>
<h1 className="text-3xl text-[#89b4fa] font-bold mb-4">
{[Link]}
</h1>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center text-[#a6adc8]">
<span>Posted by {[Link]?.username}</span>
<span className="mx-2">•</span>
<span>{new Date([Link]).toLocaleDateString()}</span>
</div>
{isAuthor && (
<div className="flex space-x-2">
<button
onClick={handleEdit}
className="p-2 rounded text-[#89b4fa] hover:text-[#74c7ec]
hover:bg-[#313244] transition-colors duration-300"
>
<svg
xmlns="[Link]
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-
2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
</svg>
</button>
<button
onClick={handleDelete}
className="p-2 rounded text-red-500 hover:text-red-400
hover:bg-[#313244] transition-colors duration-300"
>
<svg
xmlns="[Link]
className="h-5 w-5"
viewBox="0 0 448 512"
fill="currentColor"
>
<path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3
96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0
284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9
45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z" />
</svg>
</button>
</div>
)}
</div>
{[Link] && (
<img
src={[Link]}
alt={[Link]}
className="w-full h-auto rounded-lg mb-6"
/>
)}
<p className="text-[#cdd6f4] whitespace-pre-wrap">
{[Link]}
</p>
<div className="mt-6 flex items-center space-x-4">
<button
onClick={handleLike}
className={`flex items-center space-x-2 px-4 py-2 rounded-md
text-[#cdd6f4] ${
isLiked
? "bg-[#89b4fa]"
: "bg-[#313244] hover:bg-[#45475a]"
} transition-colors duration-300`}
>
<span className="text-2xl">&#10084;</span>
<span>{[Link]?.length || 0}</span>
</button>
</div>
</>
)}
</div>

<div className="mt-12">
<h2 className="text-2xl text-[#89b4fa] font-bold mb-6">Comments</h2>
<form onSubmit={handleCommentSubmit} className="mb-8">
<textarea
value={commentContent}
onChange={(e) => setCommentContent([Link])}
placeholder="Write a comment..."
className="w-full p-3 rounded bg-[#313244] text-[#cdd6f4] border
border-[#45475a] focus:outline-none focus:border-[#89b4fa] mb-4"
rows="3"
/>
<button
type="submit"
className="px-4 py-2 bg-[#89b4fa] text-[#1e1e2e] rounded hover:bg-
[#74c7ec] transition-colors duration-300"
>
Post Comment
</button>
</form>

<div className="space-y-6">
{[Link]((comment) => (
<div key={comment._id} className="bg-[#181825] p-4 rounded-lg">
<div className="flex justify-between items-start mb-2">
<div>
<span className="text-[#89b4fa] font-medium">
{[Link]?.username}
</span>
<span className="text-[#a6adc8] text-sm ml-2">
{new Date([Link]).toLocaleDateString()}
</span>
</div>
{currentUserId === [Link]?._id && (
<div className="flex space-x-2">
{editingComment === comment._id ? (
<>
<button
onClick={() => handleEditComment(comment._id)}
className="text-[#89b4fa] hover:text-[#74c7ec] text-
xl"
>
&#10003;
</button>
<button
onClick={cancelEditing}
className="text-[#a6adc8] hover:text-[#89b4fa] text-
xl"
>
&#10005;
</button>
</>
) : (
<>
<button
onClick={() => startEditing(comment)}
className="p-2 rounded text-[#89b4fa] hover:text-
[#74c7ec] hover:bg-[#313244] transition-colors duration-300"
>
<svg
xmlns="[Link]
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path d="M13.586 3.586a2 2 0 112.828
2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-
2.83-2.828z" />
</svg>
</button>
<button
onClick={() => handleDeleteComment(comment._id)}
className="p-2 rounded text-red-500 hover:text-red-
400 hover:bg-[#313244] transition-colors duration-300"
>
<svg
xmlns="[Link]
className="h-5 w-5"
viewBox="0 0 448 512"
fill="currentColor"
>
<path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0
64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8
296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6
45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z" />
</svg>
</button>
</>
)}
</div>
)}
</div>
{editingComment === comment._id ? (
<textarea
value={editContent}
onChange={(e) => setEditContent([Link])}
className="w-full p-2 rounded bg-[#313244] text-[#cdd6f4]
border border-[#45475a] focus:outline-none focus:border-[#89b4fa]"
rows="3"
/>
) : (
<p className="text-[#cdd6f4]">{[Link]}</p>
)}
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
};

export default PostPage;

You might also like