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

[Feature/#50] detail community api #57

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
29edd44
feat: ์ƒ์„ธ ๊ฒŒ์‹œ๋ฌผ api ์—ฐ๊ฒฐ ๋ฐ ๋ผ์šฐํ„ฐ ์„ค์ • - #50
zelkovaria Oct 30, 2024
1a72b08
refactor: ๊ณตํ†ต๋˜๋Š” type์„ ๋ณ„๋„์˜ interface๋กœ ๋ถ„๋ฆฌ - #50
zelkovaria Oct 30, 2024
012acc1
feat: ๊ณ ๋ฏผ ์ƒ์„ธ๊ธ€ ์กฐํšŒ์‹œ ๋ณธ๋ฌธ, ๋Œ“๊ธ€ ์ •๋ณด api ์—ฐ๊ฒฐ - #50
zelkovaria Oct 30, 2024
d7d3c12
fix: comments์˜ type์„ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์ˆ˜์ • - #50
zelkovaria Oct 30, 2024
65d4437
refactor: comment์˜ userId๋ฅผ nickname์œผ๋กœ ๊ต์ฒด - #50
zelkovaria Oct 30, 2024
1929484
fix: ์ „์ฒด ์กฐํšŒ์—์„œ ํŠน์ • ๊ฒŒ์‹œ๋ฌผ ํด๋ฆญ์‹œ ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ - #50
zelkovaria Oct 30, 2024
d1b79f3
feat: ์œ ์ € ํ”„๋กœํ•„ default image ์„ค์ • - #50
zelkovaria Oct 30, 2024
760dd2e
feat: ๊ฒŒ์‹œ๋ฌผ hover์‹œ pointer ๊ตฌํ˜„ - #50
zelkovaria Oct 30, 2024
80510ac
feat: ๋Œ“๊ธ€ ์ž…๋ ฅ์ฐฝ ๋ฒ„ํŠผ hover์‹œ pointer ๊ตฌํ˜„ - #50
zelkovaria Oct 30, 2024
75ea60c
feat: ์ƒ์„ธ ๊ฒŒ์‹œ๋ฌผ์˜ ๊ณต๊ฐ Icon์„ ์ƒํƒœ๋ณ„๋กœ ๋ณ€๊ฒฝ - #50
zelkovaria Oct 30, 2024
18c41bb
feat: ๋Œ“๊ธ€ ์ž‘์„ฑ api ์—ฐ๊ฒฐ - #50
zelkovaria Oct 31, 2024
a399119
design: ๋Œ“๊ธ€ ์ž…๋ ฅ์ฐฝ css ์ˆ˜์ • - #50
zelkovaria Oct 31, 2024
e487144
feat: ๋Œ“๊ธ€ ์ž‘์„ฑ์ž info ๋…ธ์ถœ - #50
zelkovaria Oct 31, 2024
999e3d0
refactor: ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ ์ œ๊ฑฐ - #50
zelkovaria Oct 31, 2024
24842ab
feat: ๋Œ“๊ธ€ ํ˜•์‹ ์ œํ•œ ์ถ”๊ฐ€ - #50
zelkovaria Oct 31, 2024
9eccc4d
refactor: type convention ๋ฐ˜์˜ - #50
zelkovaria Oct 31, 2024
d15a662
fix: user_id์˜ type ์ˆ˜์ • - #50
zelkovaria Oct 31, 2024
ea731b7
feat: ๊ณต๊ฐ ํด๋ฆญ์‹œ api ์—ฐ๊ฒฐ - #50
zelkovaria Oct 31, 2024
6d15990
fix: ๊ณต๊ฐ์ˆ˜๊ฐ€ 0์ธ ๊ฒฝ์šฐ count๊ฐ€ ๋ฐ˜์˜์ด ์•ˆ๋˜๋Š” ์˜ค๋ฅ˜ - #50
zelkovaria Oct 31, 2024
2428099
fix: ๊ณต๊ฐ ์ทจ์†Œ๊ฐ€ ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š” ์˜ค๋ฅ˜ ์ˆ˜์ • - #50
zelkovaria Oct 31, 2024
ef8fd9a
fix: prev ๊ฐ’์ด undefined์ธ ๊ฒฝ์šฐ ์ œ์™ธ - #50
zelkovaria Oct 31, 2024
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
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const router = createBrowserRouter([
element: <CommunityPage />,
},
{
path: '/detail/community/:id',
path: '/community/post/:id',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

writing page์—์„œ๋„ ์ˆ˜์ •ํ• ๊ฒŒ์š”~!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๋„ด!! ์ €๋„ api ํ˜•์‹๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋ฐ”๊ฟ”๋†จ์–ด์š” ๐Ÿ‘๐Ÿป

element: <DetailCommunityPage />,
},
{
Expand Down
10 changes: 10 additions & 0 deletions src/assets/default_profile_image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 9 additions & 3 deletions src/features/community/CommunityPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BeatLoader } from 'react-spinners';
import { useCallback, useEffect, useRef, useState } from 'react';
import axios from 'axios';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useSearchParams } from 'react-router-dom';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { SortKey, PostTypes } from './types';

const CommunityPage = () => {
Expand Down Expand Up @@ -42,7 +42,7 @@ const CommunityPage = () => {
},
initialPageParam: 1,
});

const navigate = useNavigate();
const loadMoreRef = useRef<HTMLDivElement | null>(null);

const handleObserver = useCallback(
Expand Down Expand Up @@ -79,7 +79,13 @@ const CommunityPage = () => {
<DropDown isSorted={isSorted} setIsSorted={handleSortChange} />
</DropDownLayout>
{data?.pages.flatMap((page) =>
page.map((post: PostTypes) => <Post key={post.id} {...post} />)
page.map((post: PostTypes) => (
<Post
key={post.id}
{...post}
onClick={() => navigate(`/community/post/${post.id}`)}
/>
))
)}
{isFetchingNextPage && (
<LoaderWrapper>
Expand Down
50 changes: 47 additions & 3 deletions src/features/community/components/CommentInput.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,49 @@
import styled from 'styled-components';
import commentUpdate from '../../../assets/comment-update.svg';
import axios from 'axios';
import { useState } from 'react';

interface CommentInputPropTypes {
postId: number | undefined;
userId: number | undefined;
refetchComments: () => void;
}

const CommentInput = ({
postId,
userId,
refetchComments,
}: CommentInputPropTypes) => {
const [commentData, setCommentData] = useState('');

const PostComment = async () => {
const trimmedComment = commentData.trim();

if (trimmedComment.length < 1 || trimmedComment.length > 500) {
alert('๋Œ“๊ธ€์€ ๊ณต๋ฐฑ์ด ์•„๋‹Œ 1์ž ์ด์ƒ 500์ž ์ดํ•˜๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”');
return;
}

try {
await axios.post(`/api/community/post/${postId}/comment`, {
content: commentData,
user_id: userId,
});
setCommentData('');
refetchComments();
} catch (e) {
console.error('๋Œ“๊ธ€ ์ž‘์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค');
}
};

const CommentInput = () => {
return (
<Wrapper>
<InputLayout placeholder="๋Œ“๊ธ€์„ ์ž…๋ ฅํ•˜์„ธ์š”" />
<img src={commentUpdate} alt="updateBtn" />
<InputLayout
placeholder="๋Œ“๊ธ€์„ ์ž…๋ ฅํ•˜์„ธ์š”"
value={commentData}
onChange={(e) => setCommentData(e.target.value)}
/>
<img src={commentUpdate} alt="updateBtn" onClick={PostComment} />
</Wrapper>
);
};
Expand All @@ -19,13 +57,19 @@ const Wrapper = styled.div`
width: 100%;
height: 64px;
background-color: ${({ theme }) => theme.colors.write_purple200};

img {
cursor: pointer;
}
`;

const InputLayout = styled.textarea`
border: none;
background-color: transparent;
width: 100%;
margin-right: 4px;
color: #ffffff;
resize: none;

::placeholder {
font-size: 1rem;
Expand Down
13 changes: 9 additions & 4 deletions src/features/community/components/Comments.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import styled from 'styled-components';
import { mockPosts } from '../../../features/community/components/mockData';

const Comments = () => {
const Comments = ({
nickname,
content,
}: {
nickname: string;
content?: string;
}) => {
return (
<Wrapper>
<p className="nickname">{mockPosts[0].author.nickname}</p>
<p className="comment">{mockPosts[0].comment}</p>
<p className="nickname">{nickname}</p>
<p className="comment">{content}</p>
</Wrapper>
);
};
Expand Down
24 changes: 20 additions & 4 deletions src/features/community/components/CommentsContent.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import styled from 'styled-components';
import { mockPosts } from '../../../features/community/components/mockData';
import { Comments, CommentInput } from './index';
import { CommentsContentPropTypes } from '../types';

const CommentsContent = () => {
const CommentsContent = ({
comment,
postId,
userId,
refetchComments,
}: CommentsContentPropTypes) => {
return (
<Wrapper>
<h2>๋Œ“๊ธ€</h2>
{mockPosts[0].comment && <Comments />}
<CommentInput />
{comment &&
comment.map((comment) => (
<Comments
key={comment.comment_id}
nickname={comment.nickname}
content={comment.content}
/>
))}
<CommentInput
postId={postId}
userId={userId}
refetchComments={refetchComments}
/>
</Wrapper>
);
};
Expand Down
84 changes: 77 additions & 7 deletions src/features/community/components/DetailPostContent.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,86 @@
import styled from 'styled-components';
import { UserInfo } from './index';
import { UserDataType } from '../types';
import { Icon } from '../../../components/ui/Icon';
import { mockPosts } from '../../../features/community/components/mockData';
import { useMutation } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import axios from 'axios';
import { useParams } from 'react-router-dom';
import { queryClient } from '../../../network/react-query/queryClient';

const DetailPostContent = ({
content,
likesCount,
userInfo,
user_id,
}: {
content?: string;
likesCount?: number;
userInfo?: UserDataType;
user_id?: number;
}) => {
const [isLiked, setIsLiked] = useState(false);
const [currentLikes, setCurrentLikes] = useState(
likesCount === 0 ? 0 : likesCount
);
const { id } = useParams<{ id: string }>();
const post_id = Number(id);

const empathyMutation = useMutation({
mutationFn: async () => {
const response = await axios.patch(
`/api/community/post/${post_id}/like`,
{},
{
params: {
user_id,
},
}
);

const previousEmpathy = queryClient.getQueryData(['empathy']);
return { ...response.data, previousEmpathy };
},
onSuccess: (data) => {
if (data.is_cancled) {
setIsLiked(false);
setCurrentLikes((prev) => (prev ?? 0) - 1);
} else {
setIsLiked(true);
setCurrentLikes((prev) => (prev ?? 0) + 1);
}
},
onError: () => {
queryClient.invalidateQueries({ queryKey: ['empathy'] });
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['empathy'] });
},
});

const handleLikeToggle = () => {
if (!isLiked) {
empathyMutation.mutate();
} else {
setIsLiked(false);
setCurrentLikes((prev) => Math.max(0, (prev ?? 0) - 1));
}
};

useEffect(() => {
setCurrentLikes(likesCount === 0 ? 0 : likesCount);
}, [likesCount]);

const DetailPostContent = () => {
return (
<Wrapper>
<UserInfo />
<Content>{mockPosts[0].content}</Content>
<EmpathyLayout>
<Icon type="community_filed_heart" alt="filledHeart" />
<p>{mockPosts[0].likes}</p>
<UserInfo user_info={userInfo} />
<Content>{content}</Content>
<EmpathyLayout onClick={handleLikeToggle}>
<Icon
type={isLiked ? 'community_filed_heart' : 'community_empty_heart'}
alt={isLiked ? 'filledHeart' : 'emptyHeart'}
/>
<p>{currentLikes}</p>
</EmpathyLayout>
</Wrapper>
);
Expand Down
9 changes: 6 additions & 3 deletions src/features/community/components/Post.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import styled from 'styled-components';
import { PostContent, PostReaction, UserInfo } from './index';
import { PostTypes } from '../types';
import { PostProps } from '../types';

const Post = (postProps: PostProps) => {
const { onClick, ...props } = postProps;

const Post = (props: Partial<PostTypes>) => {
return (
<Wrapper>
<Wrapper onClick={onClick}>
{props.user_info && props.created_at ? (
<UserInfo user_info={props.user_info} created_at={props.created_at} />
) : (
Expand All @@ -29,6 +31,7 @@ const Wrapper = styled.div`
background-color: rgba(231, 225, 255, 0.4);
margin-bottom: 36px;
padding: 20px 26px;
cursor: pointer;
`;

export default Post;
6 changes: 5 additions & 1 deletion src/features/community/components/UserInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from 'styled-components';
import { UserDataType } from '../types';
import default_profile_image from '../../../assets/default_profile_image.svg';

const UserInfo = ({
user_info,
Expand All @@ -13,7 +14,10 @@ const UserInfo = ({
<UserLayout>
{user_info && (
<>
<img src={user_info.profile_image} alt="userImg" />
<img
src={user_info.profile_image || default_profile_image}
alt="userImg"
/>
<p>{user_info.nickname}</p>
</>
)}
Expand Down
21 changes: 19 additions & 2 deletions src/features/community/components/WorryContent.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import styled from 'styled-components';
import { UserDataType } from '../types';
import { DetailPostContent } from './index';

const WorryContent = () => {
const WorryContent = ({
userInfo,
content,
likesCount,
userId,
}: {
userInfo?: UserDataType;
content?: string;
likesCount?: number;
userId?: number;
}) => {
return (
<Wrapper>
<h2>๊ณ ๋ฏผ</h2>
<DetailPostContent />
<DetailPostContent
userInfo={userInfo}
content={content}
likesCount={likesCount}
user_id={userId}
/>
</Wrapper>
);
};
Expand All @@ -14,6 +30,7 @@ const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 16px;

h2 {
font-size: 20px;
font-weight: 600;
Expand Down
36 changes: 0 additions & 36 deletions src/features/community/components/mockData.ts

This file was deleted.

Loading