From decd75f6006be15d6d544d1558dd3b0eecae7e36 Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sun, 2 Mar 2025 17:13:09 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20username=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=20api=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/apis/blog.tsx | 6 ++- src/common/types/blog.tsx | 7 +++ src/common/util/url.tsx | 11 ++++ src/components/blog/BlogSideBar.tsx | 4 +- src/components/common/Header.tsx | 10 ++-- src/components/common/ProfileImageCircle.tsx | 31 +++++++++++ src/components/post/PostCard.tsx | 2 +- src/pages/MainPage.tsx | 7 +-- src/pages/blog/BlogPage.tsx | 55 +++++++++++++------- src/pages/post/PostPage.tsx | 16 +++--- src/pages/post/SearchPage.tsx | 34 ++++++------ src/pages/post/WritePage.tsx | 4 +- src/pages/setting/PostSettingPage.tsx | 25 ++++++--- src/pages/setting/ProfileSettingPage.tsx | 12 ++--- 14 files changed, 146 insertions(+), 78 deletions(-) create mode 100644 src/components/common/ProfileImageCircle.tsx diff --git a/src/common/apis/blog.tsx b/src/common/apis/blog.tsx index c2ab498..e5057cb 100644 --- a/src/common/apis/blog.tsx +++ b/src/common/apis/blog.tsx @@ -1,5 +1,9 @@ -import {FolderRequest} from "../types/blog.tsx"; +import {FolderRequest, PostListMemberRequest} from "../types/blog.tsx"; import axiosApi from "./AxiosApi.tsx"; +import {postListMemberRequestToParameter} from "../util/url.tsx"; + +export const getMemberPosts = (postListMemberRequest: PostListMemberRequest) => + axiosApi.get(`/post/member${postListMemberRequestToParameter(postListMemberRequest)}`); export const saveAndUpdateFolder = (folderRequestList: FolderRequest[]) => axiosApi.put("/folders", folderRequestList); \ No newline at end of file diff --git a/src/common/types/blog.tsx b/src/common/types/blog.tsx index 326188b..1306184 100644 --- a/src/common/types/blog.tsx +++ b/src/common/types/blog.tsx @@ -72,3 +72,10 @@ export const toFolderRequestList = (folderTypeList: FolderType[]) => { return result; } + +export interface PostListMemberRequest { + username: string, + folderId: string | null, + page: number, + size: number, +} \ No newline at end of file diff --git a/src/common/util/url.tsx b/src/common/util/url.tsx index 9682b4a..0ca48af 100644 --- a/src/common/util/url.tsx +++ b/src/common/util/url.tsx @@ -1,5 +1,6 @@ import {PostListRequest} from "../types/post.tsx"; import {CommentListRequest} from "../types/comment.tsx"; +import {PostListMemberRequest} from "../types/blog.tsx"; export const postListRequestToParameter = (postListRequest: PostListRequest) => { const sorts = postListRequest.sorts.map(sort => `sorts=${sort}`).join("&"); @@ -16,3 +17,13 @@ export const commentListRequestToParameter = (commentListRequest: CommentListReq return query; } + +export const postListMemberRequestToParameter = (postListMemberRequest: PostListMemberRequest) => { + let query =`?username=${postListMemberRequest.username}&page=${postListMemberRequest.page}&size=${postListMemberRequest.size}`; + + if (postListMemberRequest.folderId) { + query += `&folderId=${postListMemberRequest.folderId}`; + } + + return query; +} \ No newline at end of file diff --git a/src/components/blog/BlogSideBar.tsx b/src/components/blog/BlogSideBar.tsx index 919665c..5565336 100644 --- a/src/components/blog/BlogSideBar.tsx +++ b/src/components/blog/BlogSideBar.tsx @@ -5,6 +5,7 @@ import {faker} from "@faker-js/faker/locale/ko"; import {OutlineLink} from "../common/OutlineButton.tsx"; import BlogTagCard from "./BlogTagCard.tsx"; import {getFolderTitle} from "../../common/util/string.tsx"; +import ProfileImageCircle from "../common/ProfileImageCircle.tsx"; function BlogSideBar({folders, username, addTag, setSelectedFolder, bgColor, side}: { folders: FolderType[], @@ -20,8 +21,7 @@ function BlogSideBar({folders, username, addTag, setSelectedFolder, bgColor, sid return (
- username +
{username}
diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx index 854649e..7977289 100644 --- a/src/components/common/Header.tsx +++ b/src/components/common/Header.tsx @@ -3,10 +3,11 @@ import {useDispatch, useSelector} from "react-redux"; import {RootState} from "../../store.tsx"; import {useEffect, useRef, useState} from "react"; import {TextLink} from "./TextButton.tsx"; -import {MdOutlinePerson, MdOutlineSearch} from "react-icons/md"; +import {MdOutlineSearch} from "react-icons/md"; import IconButton from "./IconButton.tsx"; import {getProfile, logoutApi} from "../../common/apis/member.tsx"; import {login, logout} from "../../common/slices/loginSlice.tsx"; +import ProfileImageCircle from "./ProfileImageCircle.tsx"; function Header() { @@ -79,12 +80,7 @@ function Header() { ?
- {loginState.profileUrl - ? user_image - : } +
diff --git a/src/components/common/ProfileImageCircle.tsx b/src/components/common/ProfileImageCircle.tsx new file mode 100644 index 0000000..83d3f20 --- /dev/null +++ b/src/components/common/ProfileImageCircle.tsx @@ -0,0 +1,31 @@ +import {MdOutlinePerson} from "react-icons/md"; + +function ProfileImageCircle({profileUrl, size, onClick, addStyle}: { + profileUrl: string, + size?: string, + onClick?: () => void, + addStyle?: string, +}) { + + let imageSize = "size-"; + let iconSize = "size-"; + if (size === "lg") { + imageSize += "32"; + iconSize += "28"; + } else { + imageSize += "10"; + iconSize += "6"; + } + + + if (profileUrl) { + return user_image; + } + + return ; +} + +export default ProfileImageCircle; \ No newline at end of file diff --git a/src/components/post/PostCard.tsx b/src/components/post/PostCard.tsx index a3305a1..6c06d01 100644 --- a/src/components/post/PostCard.tsx +++ b/src/components/post/PostCard.tsx @@ -6,7 +6,7 @@ import {dateToKorean} from "../../common/util/date.tsx"; import {Link} from "react-router-dom"; import {FaRegComment, FaRegHeart} from "react-icons/fa6"; -function PostCard(post: PostResponse) { +function PostCard({post}: {post: PostResponse}) { const safeContent = DOMPurify.sanitize(post.content); const url = getImgSrc(safeContent); diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx index 192d70a..48774ec 100644 --- a/src/pages/MainPage.tsx +++ b/src/pages/MainPage.tsx @@ -41,12 +41,7 @@ function MainPage() { {posts.map((post) => ( + post={post}/> ))}
diff --git a/src/pages/blog/BlogPage.tsx b/src/pages/blog/BlogPage.tsx index a2972f7..d3b6c94 100644 --- a/src/pages/blog/BlogPage.tsx +++ b/src/pages/blog/BlogPage.tsx @@ -9,13 +9,22 @@ import {FolderType} from "../../common/types/blog.tsx"; import BlogSideBar from "../../components/blog/BlogSideBar.tsx"; import IconButton from "../../components/common/IconButton.tsx"; import {PageResponse} from "../../common/types/common.tsx"; +import {getMemberPosts} from "../../common/apis/blog.tsx"; +import {PostResponse} from "../../common/types/post.tsx"; function BlogPage() { + const navigate = useNavigate(); const {username} = useParams(); const [page, setPage] = useState(0); - const [pageInfo, setPageInfo] = useState({number: 0, size: 5, totalElements: 53, totalPages: 11}); + const [pageInfo, setPageInfo] = useState({ + number: 0, + size: 3, + totalElements: 0, + totalPages: 0 + }); + const [posts, setPosts] = useState([]); const folderData: FolderType[] = [ { id: crypto.randomUUID(), @@ -119,7 +128,6 @@ function BlogPage() { }; }, []); - const navigate = useNavigate(); useEffect(() => { navigate(`/blog/${username}?folder=${selectedFolder?.id || ""}`); }, [selectedFolder]); @@ -136,10 +144,28 @@ function BlogPage() { }; }, [isOpen]); + useEffect(() => { + if (username === undefined) { + return; + } + + getMemberPosts({ + username: username, + folderId: null, + page: page, + size: pageInfo.size, + }) + .then((res) => { + setPosts(res.data.content); + setPageInfo(res.data.page); + }) + .catch(error => error.response.data.message); + }, [username, page]); + return (
+ className={`${(isOpen) ? "opacity-50 backdrop-blur-sm z-10 overflow-y-hidden" : "z-10"} w-full flex flex-col`}>
{username}의 블로그
- {[Array.from({length: 3}).map(() => ( + {posts.map((post) => ( `} - username={username || faker.animal.cat()} - tags={[{ - id: crypto.randomUUID(), - name: faker.word.sample() - }, {id: crypto.randomUUID(), name: faker.word.sample()}]} - createdAt={new Date()}/> - ))]} + key={post.id} + post={post}/> + ))} + {posts.length === 0 && +
+ 작성된 게시글이 없습니다. +
}
diff --git a/src/pages/post/PostPage.tsx b/src/pages/post/PostPage.tsx index f63adcd..063756e 100644 --- a/src/pages/post/PostPage.tsx +++ b/src/pages/post/PostPage.tsx @@ -15,6 +15,7 @@ import {getComments, saveComment, updateComment} from "../../common/apis/comment import {CommentResponse, CommentType} from "../../common/types/comment.tsx"; import {useSelector} from "react-redux"; import {RootState} from "../../store.tsx"; +import {PageResponse} from "../../common/types/common.tsx"; function PostPage() { @@ -22,8 +23,6 @@ function PostPage() { const navigate = useNavigate(); const loginState = useSelector((state: RootState) => state.loginSlice); - const pageSize = 10; - const [post, setPost] = useState({ id: "", title: "", @@ -34,7 +33,7 @@ function PostPage() { }); const [commentInput, setCommentInput] = useState(""); const [comments, setComments] = useState([]); - const [pageInfo, setPageInfo] = useState({number: 0, totalPages: 0}); + const [pageInfo, setPageInfo] = useState({number: 0, size: 10, totalPages: 0, totalElements: 0}); const [trigger, setTrigger] = useState(false); const handleCommentInput = (e: ChangeEvent) => { @@ -116,7 +115,7 @@ function PostPage() { postId: post.id, parentCommentId: parentId, page: page, - size: pageSize, + size: pageInfo.size, }) .then((res) => { setComments(prevComments => @@ -160,11 +159,11 @@ function PostPage() { postId: post.id, parentCommentId: null, page: pageInfo.number, - size: pageSize, + size: pageInfo.size, }) .then((res) => { setComments(prev => [...prev, ...getCommentType(res.data.content)]); - setPageInfo({number: pageInfo.number, totalPages: res.data.page.totalPages}); + setPageInfo(res.data.page); }) .catch((error) => alert(error.response.data.message)); }, [trigger]); @@ -219,8 +218,6 @@ function PostPage() {
-

댓글

- {loginState.isLogin ?
} +

댓글 ({pageInfo.totalElements})

{comments.map((comment, i) => )} + pageSize={pageInfo.size}/>)} {pageInfo.number + 1 < pageInfo.totalPages && - {(Array.from({length: 5}).map(() => ( - `} - username={faker.animal.cat()} - tags={[{ - id: faker.number.int().toString(), - name: faker.word.sample() - }, {id: faker.number.int().toString(), name: faker.word.sample()}]} - createdAt={new Date()}/> - )))} + {/*{(Array.from({length: 5}).map(() => (*/} + {/* `}*/} + {/* username={faker.animal.cat()}*/} + {/* tags={[{*/} + {/* id: faker.number.int().toString(),*/} + {/* name: faker.word.sample()*/} + {/* }, {id: faker.number.int().toString(), name: faker.word.sample()}]}*/} + {/* createdAt={new Date()}/>*/} + {/*)))}*/} { }} addStyle={"w-full"}/>
diff --git a/src/pages/post/WritePage.tsx b/src/pages/post/WritePage.tsx index 0d284c1..b72f4d1 100644 --- a/src/pages/post/WritePage.tsx +++ b/src/pages/post/WritePage.tsx @@ -78,7 +78,7 @@ function WritePage() { .then(() => { alert("작성되었습니다."); setExitPage(true); - navigate(`/blog/${loginState.username}username`); + navigate(`/blog/${loginState.username}`); }) .catch((error) => alert(error.response.data.message)) .finally(() => setLoading(false)); @@ -105,7 +105,7 @@ function WritePage() { .then(() => { alert("수정되었습니다."); setExitPage(true); - navigate(`/blog/${loginState.username}username`); + navigate(`/blog/${loginState.username}`); }) .catch((error) => alert(error.response.data.message)) .finally(() => setLoading(false)); diff --git a/src/pages/setting/PostSettingPage.tsx b/src/pages/setting/PostSettingPage.tsx index d9939d5..709073e 100644 --- a/src/pages/setting/PostSettingPage.tsx +++ b/src/pages/setting/PostSettingPage.tsx @@ -2,33 +2,40 @@ import BlogSearchBar from "../../components/blog/BlogSearchBar.tsx"; import {useEffect, useState} from "react"; import {MdSearch} from "react-icons/md"; import {PostResponse} from "../../common/types/post.tsx"; -import {getPosts} from "../../common/apis/post.tsx"; import {TextLink} from "../../components/common/TextButton.tsx"; import PaginationButton from "../../components/common/PaginationButton.tsx"; import {PageResponse} from "../../common/types/common.tsx"; import {fullDateToKorean} from "../../common/util/date.tsx"; +import {getMemberPosts} from "../../common/apis/blog.tsx"; +import {useSelector} from "react-redux"; +import {RootState} from "../../store.tsx"; function PostSettingPage() { + const loginState = useSelector((state: RootState) => state.loginSlice); + const [inputSearch, setInputSearch] = useState(""); const [posts, setPosts] = useState([]); const [page, setPage] = useState(0); - const [pageInfo, setPageInfo] = useState({number: 0, size: 5, totalElements: 0, totalPages: 0}); + const [pageInfo, setPageInfo] = useState({number: 0, size: 10, totalElements: 0, totalPages: 0}); useEffect(() => { - // todo: 본인 게시글 api로 변경 - getPosts({ - sorts: ["createdAt"], + if (!loginState.username) { + return; + } + + getMemberPosts({ + username: loginState.username, + folderId: null, page: page, size: pageInfo.size, - isDescending: true, }) .then((res) => { setPosts([...res.data.content]); setPageInfo(res.data.page); }) .catch((error) => alert(error.response.data.message)); - }, [page]); + }, [loginState, page]); return (
@@ -55,6 +62,10 @@ function PostSettingPage() {
))} + {posts.length === 0 && +
+ 작성된 게시글이 없습니다. +
}
diff --git a/src/pages/setting/ProfileSettingPage.tsx b/src/pages/setting/ProfileSettingPage.tsx index ab853e0..305b32b 100644 --- a/src/pages/setting/ProfileSettingPage.tsx +++ b/src/pages/setting/ProfileSettingPage.tsx @@ -1,12 +1,13 @@ import {useDispatch, useSelector} from "react-redux"; import {RootState} from "../../store.tsx"; -import {MdOutlineEdit, MdOutlinePerson} from "react-icons/md"; +import {MdOutlineEdit} from "react-icons/md"; import {ChangeEvent, useRef, useState} from "react"; import {FillButton} from "../../components/common/FillButton.tsx"; import {setUsername} from "../../common/slices/loginSlice.tsx"; import {TextButton} from "../../components/common/TextButton.tsx"; import {updateProfileImage, updateUsername} from "../../common/apis/member.tsx"; import {useNavigate} from "react-router-dom"; +import ProfileImageCircle from "../../components/common/ProfileImageCircle.tsx"; function ProfileSettingPage() { @@ -85,12 +86,7 @@ function ProfileSettingPage() { {isImageEdit && image ? Edit Profile Image - : (loginState.profileUrl - ? - Profile Image - : )} + : }