Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ 게시글, 댓글 필터링 기능 구현 #60

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/api/useGetComment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ export const getPostComments = async (postId) => {
}
};

// transformCommentData 함수도 약간 수정
const transformCommentData = (commentData) => ({
id: commentData.id,
postId: commentData.postId,
parentId: commentData.parentId,
content: commentData.content,
clean: commentData.clean ?? true, // clean 필드 추가, 기본값은 true
depth: commentData.depth,
createdAt: commentData.createdAt,
updatedAt: commentData.updatedAt,
Expand All @@ -94,5 +94,5 @@ const transformCommentData = (commentData) => ({
name: commentData.member.name,
nameEnglish: commentData.member.nameEnglish,
},
replies: [], // 초기 replies 배열 추가
replies: [],
});
60 changes: 4 additions & 56 deletions src/api/useGetPost.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const getPost = async (postId) => {
"createdAt",
"updatedAt",
"member",
"clean", // clean 필드로 수정
];

for (const field of requiredFields) {
Expand All @@ -45,9 +46,10 @@ export const getPost = async (postId) => {
content: postData.content,
createdAt: postData.createdAt,
updatedAt: postData.updatedAt,
imageUrls: postData.cloudFrontPaths || [], // cloudFrontPath 사용
s3ImageUrls: postData.s3ImagePaths || [], // s3ImagePaths 보관
imageUrls: postData.cloudFrontPaths || [],
s3ImageUrls: postData.s3ImagePaths || [],
likes: (postData.likes !== undefined ? postData.likes : 0).toString(),
clean: postData.clean, // clean 필드로 수정
author: {
id: postData.member.id,
role: postData.member.role,
Expand All @@ -66,57 +68,3 @@ export const getPost = async (postId) => {
throw error;
}
};

// useGetPosts.jsx
export const getPosts = async (pageNo = 0, pageSize = 10) => {
try {
const response = await api.get("/api/v1/posts", {
params: {
pageNo,
pageSize,
},
});

const transformedData = {
content: response.data.content.map((post) => ({
post_id: post.id,
post_title: post.title,
post_content: post.content,
created_at: post.createdAt,
updated_at: post.updatedAt,
member: {
member_id: post.member.id,
member_name: post.member.name,
member_name_english: post.member.nameEnglish,
course: post.member.course,
role: post.member.role,
},
// cloudFrontPaths 사용
imageUrls: post.cloudFrontPaths || [],
// 첫 번째 이미지를 대표 이미지로 사용
mainImageUrl: post.cloudFrontPaths?.[0] || "",
// S3 URL도 보관
s3ImageUrls: post.s3ImagePaths || [],
})),
totalPages: response.data.totalPages,
totalElements: response.data.totalElements,
size: response.data.size,
number: response.data.number,
first: response.data.first,
last: response.data.last,
};

return transformedData;
} catch (error) {
console.error("Error fetching posts:", error);
return {
content: [],
totalPages: 0,
totalElements: 0,
size: pageSize,
number: pageNo,
first: true,
last: true,
};
}
};
42 changes: 26 additions & 16 deletions src/api/useGetPosts.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,31 @@ export const getPosts = async (pageNo = 0, pageSize = 10) => {
},
});

// API 응답을 데이터베이스 스키마에 맞게 변환
console.log("API Response:", response.data); // API 응답 로깅 추가

const transformedData = {
content: response.data.content.map((post) => ({
post_id: post.id,
post_title: post.title,
post_content: post.content,
created_at: post.createdAt,
updated_at: post.updatedAt,
member: {
member_id: post.member.id,
member_name: post.member.name,
member_name_english: post.member.nameEnglish,
course: post.member.course,
role: post.member.role,
},
imageUrls: post.imageUrls || [],
})),
content: response.data.content.map((post) => {
console.log("Individual post data:", post); // 개별 게시물 데이터 로깅
return {
post_id: post.id,
post_title: post.title,
post_content: post.content,
created_at: post.createdAt,
updated_at: post.updatedAt,
clean: post.clean === undefined ? true : post.clean, // clean 필드 처리 수정
member: {
member_id: post.member.id,
member_name: post.member.name,
member_name_english: post.member.nameEnglish,
course: post.member.course,
role: post.member.role,
},
imageUrls: post.cloudFrontPaths || [],
mainImageUrl: post.cloudFrontPaths?.[0] || "",
s3ImageUrls: post.s3ImagePaths || [],
likes: post.likes?.toString() || "0",
};
}),
totalPages: response.data.totalPages,
totalElements: response.data.totalElements,
size: response.data.size,
Expand All @@ -35,6 +43,8 @@ export const getPosts = async (pageNo = 0, pageSize = 10) => {
last: response.data.last,
};

console.log("Transformed data:", transformedData); // 변환된 데이터 로깅

return transformedData;
} catch (error) {
console.error("Error fetching posts:", error);
Expand Down
35 changes: 35 additions & 0 deletions src/component/ FilteredComment.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useState } from "react";
import PropTypes from "prop-types";
import "./styles/FilteredComment.scss";

const FilteredCommentContent = ({ content, clean = true }) => {
const [isRevealed, setIsRevealed] = useState(false);

if (clean) {
return <div className="comment-content">{content}</div>;
}

return (
<div className="filtered-comment">
{!isRevealed ? (
<div className="filtered-message">
<span className="warning-text">
[구름봇에 의해 가려진 댓글입니다]
</span>
<button onClick={() => setIsRevealed(true)} className="reveal-button">
확인하기
</button>
</div>
) : (
<div className="comment-content">{content}</div>
)}
</div>
);
};

FilteredCommentContent.propTypes = {
content: PropTypes.string.isRequired,
clean: PropTypes.bool,
};

export default FilteredCommentContent;
7 changes: 6 additions & 1 deletion src/component/Comment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import { MoreHorizontal } from "lucide-react";
import CommentInput from "./CommentInput";
import { getCommentDetail } from "../api/useGetCommentDetail";
import FilteredCommentContent from "./ FilteredComment";
import {
useCreateComment,
useEditComment,
Expand Down Expand Up @@ -255,7 +256,10 @@ const Comment = ({
</div>
) : (
<>
<div className="content-text">{comment.content}</div>
<FilteredCommentContent
content={comment.content}
clean={comment.clean}
/>
<div className="comment-footer">
<span className="comment-date">
{formatDate(comment.createdAt)}
Expand Down Expand Up @@ -310,6 +314,7 @@ Comment.propTypes = {
comment: PropTypes.shape({
id: PropTypes.number.isRequired,
content: PropTypes.string.isRequired,
clean: PropTypes.bool, // clean 필드 추가
createdAt: PropTypes.string.isRequired,
parentId: PropTypes.number,
depth: PropTypes.number.isRequired,
Expand Down
44 changes: 44 additions & 0 deletions src/component/FilteredContent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useState } from "react";
import PropTypes from "prop-types";
import "./styles/FilteredContent.scss";

const FilteredContent = ({ title, content, clean = true }) => {
const [isRevealed, setIsRevealed] = useState(false);

if (clean) {
return (
<div className="content-wrapper">
<div className="title">{title}</div>
<div className="content">{content}</div>
</div>
);
}

return (
<div className="filtered-content">
{!isRevealed ? (
<div className="filtered-message">
<span className="warning-text">
[구름봇에 의해 가려진 게시물입니다]
</span>
<button onClick={() => setIsRevealed(true)} className="reveal-button">
확인하기
</button>
</div>
) : (
<div className="revealed-content">
<div className="title">{title}</div>
<div className="content">{content}</div>
</div>
)}
</div>
);
};

FilteredContent.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
clean: PropTypes.bool,
};

export default FilteredContent;
16 changes: 8 additions & 8 deletions src/component/Postlist.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import PropTypes from "prop-types";
import "./styles/Postlist.scss";
import Hearticon from "../image/Hearticon.svg";
//pr test
const Postlist = ({ id, title, imageUrl, likes }) => {
import SimpleFilteredContent from "./SimpleFilteredContent";

const Postlist = ({ id, title, imageUrl = "", likes, clean = true }) => {
return (
<div className="postlist-container" data-post-id={id}>
<div className="postlist-img">
Expand All @@ -13,14 +14,16 @@ const Postlist = ({ id, title, imageUrl, likes }) => {
onError={(e) => {
console.error("Image load error:", imageUrl);
e.target.onerror = null;
e.target.src = "/default-image.png"; // 기본 이미지 경로
e.target.src = "/default-image.png";
}}
/>
) : (
<div className="no-image"></div>
)}
</div>
<div className="postlist-title">{title}</div>
<div className="postlist-title">
<SimpleFilteredContent content={title} clean={clean} />
</div>

<div className="postlist-heart">
<img src={Hearticon} alt="하트 아이콘" />
Expand All @@ -35,10 +38,7 @@ Postlist.propTypes = {
title: PropTypes.string.isRequired,
imageUrl: PropTypes.string,
likes: PropTypes.string.isRequired,
};

Postlist.defaultProps = {
imageUrl: "",
clean: PropTypes.bool,
};

export default Postlist;
20 changes: 20 additions & 0 deletions src/component/SimpleFilteredContent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import PropTypes from "prop-types";

const SimpleFilteredContent = ({ content, clean = true }) => {
if (clean) {
return <div>{content}</div>;
}

return (
<div className="simple-filtered-content">
<span className="warning-text">[구름봇에 의해 가려졌습니다]</span>
</div>
);
};

SimpleFilteredContent.propTypes = {
content: PropTypes.string.isRequired,
clean: PropTypes.bool,
};

export default SimpleFilteredContent;
9 changes: 7 additions & 2 deletions src/component/ViewPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { deletePost } from "../api/useDeletePost";
import WriteForm from "../containers/WriteForm";
import CommentInput from "./CommentInput";
import { useCreateComment } from "../hooks/useComment";
import FilteredContent from "./FilteredContent";

const ViewPage = ({
post,
Expand Down Expand Up @@ -257,8 +258,11 @@ const ViewPage = ({
)}
</div>
<div className="form-group">
<div className="title">제목: {post.title}</div>
<div className="content">{post.content}</div>
<FilteredContent
title={post.title}
content={post.content}
clean={post.clean}
/>
<div className="reaction">
<img className="hearts" src={Hearticon} alt="하트 아이콘" />
<span>{post.likes}</span>
Expand Down Expand Up @@ -310,6 +314,7 @@ ViewPage.propTypes = {
imageUrl: PropTypes.string,
likes: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired,
clean: PropTypes.bool.isRequired, // isClean에서 clean으로 변경
author: PropTypes.shape({
id: PropTypes.number.isRequired,
role: PropTypes.string.isRequired,
Expand Down
5 changes: 3 additions & 2 deletions src/component/styles/Comment.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
margin-bottom: 15px;

.comment-header {
margin-bottom: 8px;
margin-bottom: 0px;

.author-line {
display: flex;
Expand Down Expand Up @@ -55,7 +55,8 @@
}

.comment-content {
margin: 8px 0;
margin-top: 3px;
margin-bottom: 8px;
word-break: break-word;
}

Expand Down
Loading