diff --git a/src/api/post/hooks.ts b/src/api/post/hooks.ts index 3b127076..399aa96d 100644 --- a/src/api/post/hooks.ts +++ b/src/api/post/hooks.ts @@ -25,12 +25,12 @@ export const useInfinitePosts = (take: number, meetingId: number) => { return { data, hasNextPage, fetchNextPage, isFetchingNextPage, isLoading }; }; -export const useMutationUpdateLike = (take: number, meetingId: number, postId: number) => { +export const useMutationUpdateLike = (take: number, meetingId: number) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: () => postLike(String(postId)), - onMutate: async () => { + mutationFn: (postId: number) => postLike(String(postId)), + onMutate: async postId => { await queryClient.cancelQueries(['getPosts', take, meetingId]); type Post = paths['/post/v1']['get']['responses']['200']['content']['application/json']['data']; diff --git a/src/components/button/LikeButton.tsx b/src/components/button/LikeButton.tsx new file mode 100644 index 00000000..56b06395 --- /dev/null +++ b/src/components/button/LikeButton.tsx @@ -0,0 +1,42 @@ +import LikeActiveIcon from '@assets/svg/like_active.svg'; +import LikeDefaultIcon from '@assets/svg/like_default.svg'; +import { LIKE_MAX_COUNT } from '@constants/feed'; +import { styled } from 'stitches.config'; + +interface LikeButtonProps { + isLiked: boolean; + likeCount: number; + onClickLike: (e: React.MouseEvent) => void; +} + +export default function LikeButton({ isLiked, likeCount, onClickLike }: LikeButtonProps) { + const formattedLikeCount = likeCount > LIKE_MAX_COUNT ? `${LIKE_MAX_COUNT}+` : likeCount; + + return ( + + {isLiked ? : } + {formattedLikeCount} + + ); +} + +const SLikeButton = styled('button', { + display: 'flex', + alignItems: 'center', + fontStyle: 'H5', + + variants: { + like: { + true: { + color: '$red', + }, + false: { + color: '$gray10', + }, + }, + }, + + '& > svg': { + mr: '$6', + }, +}); diff --git a/src/components/page/meetingDetail/Feed/FeedItem/index.tsx b/src/components/page/meetingDetail/Feed/FeedItem/index.tsx index 920e0d39..f5e7e24b 100644 --- a/src/components/page/meetingDetail/Feed/FeedItem/index.tsx +++ b/src/components/page/meetingDetail/Feed/FeedItem/index.tsx @@ -4,19 +4,10 @@ import { styled } from 'stitches.config'; // import MoreIcon from '@assets/svg/more.svg'; import { ampli } from '@/ampli'; import { useQueryGetMeeting } from '@api/meeting/hooks'; -import { useMutationUpdateLike } from '@api/post/hooks'; import { UserResponse } from '@api/user'; -import LikeActiveIcon from '@assets/svg/like_active.svg'; -import LikeDefaultIcon from '@assets/svg/like_default.svg'; import ProfileDefaultIcon from '@assets/svg/profile_default.svg?rect'; import Avatar from '@components/avatar/Avatar'; -import { - AVATAR_MAX_LENGTH, - CARD_CONTENT_MAX_LENGTH, - CARD_TITLE_MAX_LENGTH, - LIKE_MAX_COUNT, - TAKE_COUNT, -} from '@constants/feed'; +import { AVATAR_MAX_LENGTH, CARD_CONTENT_MAX_LENGTH, CARD_TITLE_MAX_LENGTH } from '@constants/feed'; import { THUMBNAIL_IMAGE_INDEX } from '@constants/index'; import { useDisplay } from '@hooks/useDisplay'; import { playgroundLink } from '@sopt-makers/playground-common'; @@ -40,40 +31,20 @@ interface PostProps { interface FeedItemProps { post: PostProps; HeaderSection?: React.ReactNode; + LikeButton?: React.ReactNode; + meetingId?: number; + onClick?: (e: React.MouseEvent) => void; } -const FeedItem = ({ post, HeaderSection }: FeedItemProps) => { - const { id, user, title, contents, images, updatedDate, commenterThumbnails, commentCount, likeCount, isLiked } = - post; - const formattedLikeCount = likeCount > LIKE_MAX_COUNT ? `${LIKE_MAX_COUNT}+` : likeCount; +const FeedItem = ({ post, HeaderSection, LikeButton, meetingId: _meetingId, onClick }: FeedItemProps) => { + const { user, title, contents, images, updatedDate, commenterThumbnails, commentCount } = post; const router = useRouter(); - const meetingId = router.query.id as string; - const { data: meeting } = useQueryGetMeeting({ params: { id: meetingId } }); - const { mutate } = useMutationUpdateLike(TAKE_COUNT, Number(meetingId), id); - const { isMobile } = useDisplay(); + // NOTE: 게시글 상세페이지에선 router.query.id 가 post의 id 이기 때문에 meetingId를 주입받아야 한다. + const meetingId = _meetingId ?? Number(router.query.id as string); + const { data: meeting } = useQueryGetMeeting({ params: { id: String(meetingId) } }); - const handleLikeClick = (e: React.MouseEvent) => { - e.preventDefault(); - mutate(); - ampli.clickFeedlistLike({ crew_status: meeting?.approved, location: router.pathname }); - }; return ( - - ampli.clickFeedCard({ - feed_id: id, - feed_upload: updatedDate, - feed_title: title, - feed_image_total: images ? images.length : 0, - feed_comment_total: commentCount, - feed_like_total: likeCount, - group_id: Number(meetingId), - crew_status: meeting?.approved, - platform_type: isMobile ? 'MO' : 'PC', - location: router.pathname, - }) - } - > + {HeaderSection} @@ -129,10 +100,7 @@ const FeedItem = ({ post, HeaderSection }: FeedItemProps) => { - - {isLiked ? : } - {formattedLikeCount} - + {LikeButton} ); @@ -281,27 +249,6 @@ const SCommentCount = styled('span', { fontStyle: 'H5', }); -const SLikeButton = styled('button', { - display: 'flex', - alignItems: 'center', - fontStyle: 'H5', - - variants: { - like: { - true: { - color: '$red', - }, - false: { - color: '$gray10', - }, - }, - }, - - '& > svg': { - mr: '$6', - }, -}); - const SOverlay = styled('div', { position: 'absolute', background: '$gray950', diff --git a/src/components/page/meetingDetail/Feed/FeedPanel.tsx b/src/components/page/meetingDetail/Feed/FeedPanel.tsx index 840b7867..754ef219 100644 --- a/src/components/page/meetingDetail/Feed/FeedPanel.tsx +++ b/src/components/page/meetingDetail/Feed/FeedPanel.tsx @@ -1,5 +1,5 @@ import { ampli } from '@/ampli'; -import { useInfinitePosts } from '@api/post/hooks'; +import { useInfinitePosts, useMutationUpdateLike } from '@api/post/hooks'; import { useQueryMyProfile } from '@api/user/hooks'; import FeedCreateModal from '@components/feed/Modal/FeedCreateModal'; import { POST_MAX_COUNT, TAKE_COUNT } from '@constants/feed'; @@ -14,6 +14,8 @@ import { styled } from 'stitches.config'; import EmptyView from './EmptyView'; import FeedItem from './FeedItem'; import MobileFeedListSkeleton from './Skeleton/MobileFeedListSkeleton'; +import LikeButton from '@components/button/LikeButton'; +import { useQueryGetMeeting } from '@api/meeting/hooks'; interface FeedPanelProps { isMember: boolean; @@ -24,7 +26,7 @@ const FeedPanel = ({ isMember }: FeedPanelProps) => { const meetingId = router.query.id as string; const feedCreateOverlay = useOverlay(); - const { isTablet } = useDisplay(); + const { isMobile, isTablet } = useDisplay(); const { data: me } = useQueryMyProfile(); const { data: postsData, @@ -34,6 +36,8 @@ const FeedPanel = ({ isMember }: FeedPanelProps) => { isLoading, } = useInfinitePosts(TAKE_COUNT, Number(meetingId)); useScrollRestorationAfterLoading(isLoading); + const { data: meeting } = useQueryGetMeeting({ params: { id: meetingId } }); + const { mutate: mutateLike } = useMutationUpdateLike(TAKE_COUNT, Number(meetingId)); const isEmpty = !postsData?.pages[0]; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -56,17 +60,43 @@ const FeedPanel = ({ isMember }: FeedPanelProps) => { }); }; - const renderedPosts = postsData?.pages.map(post => ( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - - - - {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} - {/* @ts-ignore */} - - - - )); + const handleLikeClick = (postId: number) => (e: React.MouseEvent) => { + e.preventDefault(); + mutateLike(postId); + ampli.clickFeedlistLike({ crew_status: meeting?.approved, location: router.pathname }); + }; + + const renderedPosts = postsData?.pages.map(post => { + if (!post) return; + return ( + + + + } + onClick={() => + ampli.clickFeedCard({ + feed_id: post.id, + feed_upload: post.updatedDate, + feed_title: post.title, + feed_image_total: post.images ? post.images.length : 0, + feed_comment_total: post.commentCount, + feed_like_total: post.likeCount, + group_id: Number(meetingId), + crew_status: meeting?.approved, + platform_type: isMobile ? 'MO' : 'PC', + location: router.pathname, + }) + } + /> + + + ); + }); return ( <>