From 72066f2df2aa5cd50f568ca64e6b27d0c41b612a Mon Sep 17 00:00:00 2001 From: yeonghun Date: Fri, 31 May 2024 00:00:53 +0900 Subject: [PATCH] week10 hw --- src/apis/api.js | 44 ++++++-- src/apis/axios.js | 21 ++-- src/components/Comment/CommentElement.jsx | 132 +++++++++++++--------- src/components/Comment/index.jsx | 52 +++++---- src/components/Posts/index.jsx | 39 ++++--- src/routes/PostCreatePage.jsx | 3 +- 6 files changed, 171 insertions(+), 120 deletions(-) diff --git a/src/apis/api.js b/src/apis/api.js index af51a909..bf9cec9f 100644 --- a/src/apis/api.js +++ b/src/apis/api.js @@ -1,4 +1,4 @@ -import { instance, instanceWithToken } from './axios'; +import { instance, instanceWithToken } from "./axios"; // Account 관련 API들 export const signIn = async (data) => { @@ -64,7 +64,7 @@ export const updatePost = async (id, data, navigate) => { // 과제!! export const deletePost = async (id, navigate) => { const response = await instanceWithToken.delete(`/post/${id}/`); - if(response.status === 204) { + if (response.status === 204) { console.log("DELETE SUCCESS"); navigate(-1); } else { @@ -74,7 +74,17 @@ export const deletePost = async (id, navigate) => { // 과제!! export const likePost = async (postId) => { - + try { + const response = await instanceWithToken.post(`/post/${postId}/like/`); + if (response.status === 200) { + console.log("LIKE SUCCESS"); + window.location.reload(); + } else { + console.log("[ERROR] error while liking post"); + } + } catch (error) { + console.log(error); + } }; // Tag 관련 API들 @@ -100,17 +110,21 @@ export const getComments = async (postId) => { }; export const createComment = async (data) => { - const response = await instanceWithToken.post("/comment/", data); - if (response.status === 201) { - console.log("COMMENT SUCCESS"); - window.location.reload(); // 새로운 코멘트 생성시 새로고침으로 반영 - } else { - console.log("[ERROR] error while creating comment"); + try { + const response = await instanceWithToken.post("/comment/", data); + if (response.status === 201) { + console.log("COMMENT SUCCESS"); + window.location.reload(); // 새로운 코멘트 생성시 새로고침으로 반영 + } else { + console.log("[ERROR] error while creating comment"); + } + } catch (error) { + console.log(error); } }; export const updateComment = async (id, data) => { - const response = await instanceWithToken.put(`/comment/${id}/`, data);// 혹시 patch로 구현했다면 .patch + const response = await instanceWithToken.put(`/comment/${id}/`, data); // 혹시 patch로 구현했다면 .patch if (response.status === 200) { console.log("COMMENT UPDATE SUCCESS"); window.location.reload(); @@ -121,5 +135,11 @@ export const updateComment = async (id, data) => { // 과제 !! export const deleteComment = async (id) => { - -}; \ No newline at end of file + const response = await instanceWithToken.delete(`/comment/${id}/`); + if (response.status === 204) { + console.log("DELETE SUCCESS"); + window.location.reload(); // 코멘트 삭제시 새로고침으로 반영 + } else { + console.log("[ERROR] error while deleting comment"); + } +}; diff --git a/src/apis/axios.js b/src/apis/axios.js index d2f85b9a..f0d76198 100644 --- a/src/apis/axios.js +++ b/src/apis/axios.js @@ -1,13 +1,13 @@ import axios from "axios"; import { getCookie } from "../utils/cookie"; -// baseURL, credential, 헤더 세팅 -axios.defaults.baseURL = 'http://localhost:8000/api'; +// baseURL, credential, 헤더 세팅 +axios.defaults.baseURL = "http://localhost:8000/api"; axios.defaults.withCredentials = true; -axios.defaults.headers.post['Content-Type'] = 'application/json'; -axios.defaults.headers.common['X-CSRFToken'] = getCookie('csrftoken'); +axios.defaults.headers.post["Content-Type"] = "application/json"; +axios.defaults.headers.common["X-CSRFToken"] = getCookie("csrftoken"); -// 누구나 접근 가능한 API들 +// 누구나 접근 가능한 API들 export const instance = axios.create(); // Token 있어야 접근 가능한 API들 - 얘는 토큰을 넣어줘야 해요 @@ -16,13 +16,14 @@ export const instanceWithToken = axios.create(); // instanceWithToken에는 쿠키에서 토큰을 찾고 담아줍시다! instanceWithToken.interceptors.request.use( // 요청을 보내기전 수행할 일 - // 사실상 이번 세미나에 사용할 부분은 이거밖에 없어요 + // 사실상 이번 세미나에 사용할 부분은 이거밖에 없어요 (config) => { - const accessToken = getCookie('access_token'); + const accessToken = getCookie("access_token"); if (!accessToken) { // token 없으면 리턴 - return; + alert("Please login first."); + throw new Error("No Token"); } else { // token 있으면 헤더에 담아주기 (Authorization은 장고에서 JWT 토큰을 인식하는 헤더 key) config.headers["Authorization"] = `Bearer ${accessToken}`; @@ -40,7 +41,7 @@ instanceWithToken.interceptors.request.use( instanceWithToken.interceptors.response.use( (response) => { - // 서버 응답 데이터를 프론트에 넘겨주기 전 수행할 일 + // 서버 응답 데이터를 프론트에 넘겨주기 전 수행할 일 console.log("Interceptor Response!!"); return response; }, @@ -49,4 +50,4 @@ instanceWithToken.interceptors.response.use( console.log("Response Error!!"); return Promise.reject(error); } -); \ No newline at end of file +); diff --git a/src/components/Comment/CommentElement.jsx b/src/components/Comment/CommentElement.jsx index fd48385e..fa425a14 100644 --- a/src/components/Comment/CommentElement.jsx +++ b/src/components/Comment/CommentElement.jsx @@ -1,59 +1,83 @@ import { useState, useEffect } from "react"; +import { updateComment, getUser } from "../../apis/api"; +import { getCookie } from "../../utils/cookie"; const CommentElement = (props) => { - const { comment, handleCommentDelete, postId } = props; - const [content, setContent] = useState(comment.content); - const [isEdit, setIsEdit] = useState(false); - - const [onChangeValue, setOnChangeValue] = useState(content); // 수정 취소 시 직전 content 값으로 변경을 위한 state - - // comment created_at 전처리 - const date = new Date(comment.created_at); - const year = date.getFullYear(); - let month = date.getMonth() + 1; - month = month < 10 ? `0${month}` : month; - let day = date.getDate(); - day = day < 10 ? `0${day}` : day; - - const handleEditComment = () => { // add api call for editing comment - setContent(onChangeValue); - setIsEdit(!isEdit); - console.log({ - post: postId, - comment: comment.id, - content: content - }); - }; - - useEffect(() => { // add api call to check if user is the author of the comment - }, []); - - return ( -
-
- {isEdit ? ( - setOnChangeValue(e.target.value)} /> - ) : ( -

{content}

- )} - - {year}.{month}.{day} -
- -
- {isEdit ? ( - <> - - - - ) : ( - <> - - - - )} -
-
- ); + const { comment, handleCommentDelete, postId } = props; + const [content, setContent] = useState(comment.content); + const [isEdit, setIsEdit] = useState(false); + const [user, setUser] = useState(null); // state for user + + const [onChangeValue, setOnChangeValue] = useState(content); // 수정 취소 시 직전 content 값으로 변경을 위한 state + + // comment created_at 전처리 + const date = new Date(comment.created_at); + const year = date.getFullYear(); + let month = date.getMonth() + 1; + month = month < 10 ? `0${month}` : month; + let day = date.getDate(); + day = day < 10 ? `0${day}` : day; + + const handleEditComment = async () => { + await updateComment(comment.id, { content: onChangeValue }); + setIsEdit(!isEdit); + }; + + // get user info + useEffect(() => { + if (getCookie("access_token")) { + const getUserAPI = async () => { + const user = await getUser(); + setUser(user); + }; + getUserAPI(); + } + }, []); + + return ( +
+
+ {isEdit ? ( + setOnChangeValue(e.target.value)} + /> + ) : ( +

{content}

+ )} + + + {year}.{month}.{day} + +
+ +
+ {user?.id === comment.author.id ? ( + isEdit ? ( + <> + + + + ) : ( + <> + + + + ) + ) : null} + {} +
+
+ ); }; export default CommentElement; diff --git a/src/components/Comment/index.jsx b/src/components/Comment/index.jsx index c8c4a27e..4306f0f1 100644 --- a/src/components/Comment/index.jsx +++ b/src/components/Comment/index.jsx @@ -1,36 +1,38 @@ -import { useState } from "react"; -import comments from "../../data/comments"; // dummy data +import { useState, useEffect } from "react"; import CommentElement from "./CommentElement"; +import { getComments, createComment, deleteComment } from "../../apis/api"; const Comment = ({ postId }) => { - const [commentList, setCommentList] = useState(comments); // state for comments + const [commentList, setCommentList] = useState([]); // state for comments const [newContent, setNewContent] = useState(""); // state for new comment - const handleCommentSubmit = (e) => { + // get comments of the post + useEffect(() => { + const getCommentsAPI = async () => { + const comments = await getComments(postId); + setCommentList(comments); + } + getCommentsAPI(); + }, [postId]); + + const handleCommentSubmit = async (e) => { e.preventDefault(); - setCommentList([ // TODO: add api call for creating comment - ...commentList, - { - id: commentList.length + 1, - content: newContent, - created_at: new Date().toISOString(), - post: postId, - author: { - id: 1, - username: "user1" - } - } - ]); - console.log({ - post: postId, - content: newContent - }); + // add new comment to the list + await createComment({post: postId , content: newContent}); + // reset newContent setNewContent(""); + // comments will be updated by reloading the page in createComment function }; - const handleCommentDelete = (commentId) => { - console.log("comment: ", commentId); - setCommentList(commentList.filter((comment) => comment.id !== commentId)); // TODO: add api call for deleting comment + const handleCommentDelete = async (commentId) => { + const confirmDelete = window.confirm("정말 삭제하시겠습니까?"); + if(!confirmDelete) return; + + try { + await deleteComment(commentId); + } catch (error) { + console.error(error); + } }; return ( @@ -50,4 +52,4 @@ const Comment = ({ postId }) => { ); }; -export default Comment; +export default Comment; \ No newline at end of file diff --git a/src/components/Posts/index.jsx b/src/components/Posts/index.jsx index ca0e0bac..e20de3aa 100644 --- a/src/components/Posts/index.jsx +++ b/src/components/Posts/index.jsx @@ -1,10 +1,12 @@ import { Link } from "react-router-dom"; +import { likePost } from "../../apis/api"; export const SmallPost = ({ post }) => { - const onClickLike = () => { - alert("나도 좋아!"); - // add api call for liking post here + const onClickLike = async (e) => { + e.preventDefault(); + await likePost(post.id); }; + return ( { ))} -
- {post.like_users.length > 0 && `❤️ ${post.like_users.length}`} -
+ ); }; export const BigPost = ({ post }) => { - const onClickLike = () => { - alert("나도 좋아!"); - // add api call for liking post here + const onClickLike = async (e) => { + e.preventDefault(); + await likePost(post.id); }; + return (
@@ -41,11 +48,9 @@ export const BigPost = ({ post }) => { {post.created_at.slice(0, 10)}
- -
+
{post.content}
-
{post.tags && post.tags.map((tag) => ( @@ -54,13 +59,13 @@ export const BigPost = ({ post }) => { ))}
- -
- {post.like_users.length > 0 && `❤️ ${post.like_users.length}`} -
+ ❤️ {post.like_users.length} +
); }; diff --git a/src/routes/PostCreatePage.jsx b/src/routes/PostCreatePage.jsx index cc06ef46..34fec1cc 100644 --- a/src/routes/PostCreatePage.jsx +++ b/src/routes/PostCreatePage.jsx @@ -5,7 +5,6 @@ import { getTags, createPost } from "../apis/api"; import { useNavigate } from "react-router-dom"; const PostCreatePage = () => { - const [isSubmitted, setIsSubmitted] = useState(false); const [post, setPost] = useState({ title: "", content: "", @@ -83,7 +82,7 @@ const PostCreatePage = () => { const onSubmit = (e) => { e.preventDefault(); createPost(post, navigate); - } + }; return (