-
Notifications
You must be signed in to change notification settings - Fork 6
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
Feat: 마이리스트, 콜라보리스트 페이지 팔로우 기능 구현 #26
Changes from 10 commits
a777db2
a3954e4
62a3968
279bb40
f796bb8
e8d9b71
77d5e48
e0baf4d
4bf31f4
68edd47
3152de5
699f147
d3e9463
e86d9f8
0d8b8c5
4d7a098
a2ba72d
6b13628
5fd79a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
const createFollowUser = async (userId: number) => { | ||
return await axiosInstance.post(`/follow/${userId}`); | ||
}; | ||
|
||
export default createFollowUser; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
|
||
const deleteFollowUser = async (userId: number) => { | ||
return await axiosInstance.delete(`/follow/${userId}`); | ||
}; | ||
|
||
export default deleteFollowUser; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import axiosInstance from '@/lib/axios/axiosInstance'; | ||
import { UserType } from '@/lib/types/userProfileType'; | ||
|
||
export const getUserOne = async (userId: number) => { | ||
const getUserOne = async (userId: number) => { | ||
const response = await axiosInstance.get<UserType>(`/users/${userId}`); | ||
|
||
return response.data; | ||
}; | ||
|
||
export default getUserOne; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,26 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
import { style, styleVariants } from '@vanilla-extract/css'; | ||
import { vars } from '@/styles/theme.css'; | ||
|
||
export const button = style({ | ||
padding: '0.8rem 1.2rem', | ||
|
||
backgroundColor: vars.color.blue, | ||
borderRadius: '5rem', | ||
fontWeight: '400', | ||
lineHeight: '1.6rem', | ||
}); | ||
|
||
fontSize: '1rem', | ||
fontWeight: '600', | ||
color: vars.color.white, | ||
export const variant = styleVariants({ | ||
primary: [ | ||
button, | ||
{ | ||
backgroundColor: vars.color.blue, | ||
color: vars.color.white, | ||
}, | ||
], | ||
gray: [ | ||
button, | ||
{ | ||
backgroundColor: vars.color.gray7, | ||
color: vars.color.white, | ||
}, | ||
], | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,83 @@ | ||
'use client'; | ||
|
||
/** | ||
TODO | ||
- [ ] 상태(팔로우, 언팔로우)에 따른 팔로우 버튼 UI | ||
- [ ] 조건(비회원, 회원)에 따른 팔로우 버튼 동작(api 연동) | ||
*/ | ||
import { useRouter } from 'next/navigation'; | ||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | ||
import { AxiosError } from 'axios'; | ||
|
||
import * as styles from './FollowButton.css'; | ||
|
||
interface ActionProps { | ||
import createFollowUser from '@/app/_api/follow/createFollowUser'; | ||
import deleteFollowUser from '@/app/_api/follow/deleteFollowUser'; | ||
import getUserOne from '@/app/_api/user/getUserOne'; | ||
|
||
import { QUERY_KEYS } from '@/lib/constants/queryKeys'; | ||
import { UserType } from '@/lib/types/userProfileType'; | ||
import { useUser } from '@/store/useUser'; | ||
import toasting from '@/lib/utils/toasting'; | ||
|
||
interface FollowButtonProps { | ||
userId: number; | ||
isFollowed: boolean; | ||
} | ||
|
||
export default function FollowButton({ isFollowed }: ActionProps) { | ||
const label = isFollowed ? '팔로우' : '팔로우 취소'; | ||
const MAX_FOLLOWING = 1000; | ||
|
||
export default function FollowButton({ isFollowed, userId }: FollowButtonProps) { | ||
const queryClient = useQueryClient(); | ||
const router = useRouter(); | ||
const { user: userMe } = useUser(); | ||
|
||
const { data: userMeData } = useQuery<UserType>({ | ||
queryKey: [QUERY_KEYS.userOne, userMe.id], | ||
queryFn: () => getUserOne(userMe.id), | ||
enabled: !!userMe.id, | ||
}); | ||
|
||
const followUser = useMutation({ | ||
mutationKey: [QUERY_KEYS.follow, userId], | ||
mutationFn: () => createFollowUser(userId), | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ | ||
queryKey: [QUERY_KEYS.userOne, userId], | ||
}); | ||
}, | ||
onError: (error: AxiosError) => { | ||
if (error.response?.status === 401) { | ||
toasting({ type: 'warning', txt: '로그인이 필요한 기능입니다.' }); | ||
router.push('/login'); | ||
} | ||
}, | ||
}); | ||
|
||
const deleteFollowingUser = useMutation({ | ||
mutationKey: [QUERY_KEYS.deleteFollow, userId], | ||
mutationFn: () => deleteFollowUser(userId), | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ | ||
queryKey: [QUERY_KEYS.userOne, userId], | ||
}); | ||
}, | ||
}); | ||
|
||
const handleFollowUser = (isFollowed: boolean) => () => { | ||
if (userMeData && userMeData?.followingCount >= MAX_FOLLOWING) { | ||
toasting({ type: 'warning', txt: '최대 1000명까지 팔로우할 수 있어요.' }); | ||
return; | ||
} | ||
|
||
const handleFollowUser = () => { | ||
// 1. follow 하는 api 요청 + update | ||
if (isFollowed) { | ||
deleteFollowingUser.mutate(); | ||
} else { | ||
followUser.mutate(); | ||
} | ||
}; | ||
|
||
return ( | ||
<button className={styles.button} onClick={handleFollowUser}> | ||
{label} | ||
<button | ||
className={`${isFollowed ? styles.variant.gray : styles.variant.primary}`} | ||
onClick={handleFollowUser(isFollowed)} | ||
> | ||
{isFollowed ? '팔로우 취소' : '팔로우'} | ||
</button> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ import FollowButton from './FollowButton'; | |
import SettingIcon from '/public/icons/setting.svg'; | ||
|
||
import useMoveToPage from '@/hooks/useMoveToPage'; | ||
import { getUserOne } from '@/app/_api/user/getUserOne'; | ||
import getUserOne from '@/app/_api/user/getUserOne'; | ||
import { QUERY_KEYS } from '@/lib/constants/queryKeys'; | ||
import { UserType } from '@/lib/types/userProfileType'; | ||
|
||
|
@@ -78,15 +78,15 @@ export default function Profile({ userId }: { userId: number }) { | |
<div className={styles.info}> | ||
<div className={styles.user}> | ||
<span className={styles.nickName}>{data?.nickname}</span> | ||
{!data?.isOwner && <FollowButton isFollowed={!!data?.isFollowed} />} | ||
{!data?.isOwner && <FollowButton userId={userId} isFollowed={!!data?.isFollowed} />} | ||
</div> | ||
<div className={styles.follow}> | ||
<div className={styles.text} onClick={onClickMoveToPage(`/user/${userId}/followings`)}> | ||
<span className={styles.count}>{data?.followingCount}</span> | ||
<span className={styles.count}>{data?.followingCount.toLocaleString('ko-KR')}</span> | ||
<span>팔로잉</span> | ||
</div> | ||
<div className={styles.text} onClick={onClickMoveToPage(`/user/${userId}/followers`)}> | ||
<span className={styles.count}>{data?.followerCount}</span> | ||
<span className={styles.count}>{data?.followerCount.toLocaleString('ko-KR')}</span> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇네요 서영님!! 정책대로 축약형으로 나타내는 것으로 수정하겠습니다! (+.공통 유틸로 분리해 놓겠습니다.) +. 생각해보니 천이상 ~ 만, 십만, 백만까지가 축약이 많이된 것 같아서 다음과 같이 변경해도 괜찮을 것 같은데.. 서영님 생각은 어떠신가요?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [KO] [ENG] K: 1,000 |
||
<span>팔로워</span> | ||
</div> | ||
</div> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 다시 불러올 수 있는거군요...!
와 저 프로필 설정에서 '저장' 버튼 누른다음에 다시 fetch 해올때 딱 이 방법이 필요했어요! 지금은 수정사항 발생시 true로 바뀌었던 'isDirty'를 '저장' 버튼을 누르면 false로 바꾸는 식으로 구현했거든요! 당장 코드 바꾸러 갑니다 🙇♀️