-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: 마이리스트, 콜라보리스트 페이지 팔로우 기능 구현 (#26)
* Feat: 마이리스트, 콜라보리스트 페이지 - 프로필 팔로우 기능 구현 * Design: 팔로우 상태에따른 버튼 UI 스타일 적용 * Feat: 로그인 상태가 아닐때 에러 처리 추가 * Feat: 마이리스트, 콜라보리스트 페이지 - 프로필 팔로우 취소 기능 구현 * Feat: 최대 팔로잉 제한 조건 적용 * Fix: 마이리스트, 콜라보리스트 페이지 관련 api export문 default로 수정 * Feat: 프로필 팔로잉, 팔로우 수 천 단위 콤마 적용 * Style: 주석정리, console.log 정리 * Fix: 리스트 아이템 타입 프로퍼티 이름 수정 * Feat: 프로필 정보 불러오기 enabled 옵션 추가, 토스트 메세지 추가 * Design: 토스트 메세지 수정 * Style: 공백 수정 * Fix: dev 브랜치 merge에 따른 리스트 상세조회 타입 수정 * Feat: 토스트메세지 상수화 처리 * Feat: 팔로우 요청 제한 로직 수정 및 토스트 메세지 수정 * Feat: 숫자를 1만 단위로 축약하는 number formatter 로직 구현 * Feat: 숫자를 미국 K단위, M단위로 축약하는 number formatter 로직 구현 * Style: 숫자 포맷팅 함수에 jsdoc 주석 추가
- Loading branch information
1 parent
f269e41
commit ca45024
Showing
14 changed files
with
169 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,81 @@ | ||
'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'; | ||
import { MAX_FOLLOWING, toastMessage } from '@/lib/constants/toastMessage'; | ||
|
||
interface FollowButtonProps { | ||
userId: number; | ||
isFollowed: boolean; | ||
} | ||
|
||
export default function FollowButton({ isFollowed }: ActionProps) { | ||
const label = isFollowed ? '팔로우' : '팔로우 취소'; | ||
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: toastMessage.ko.requiredLogin }); | ||
router.push('/login'); | ||
} | ||
}, | ||
}); | ||
|
||
const deleteFollowingUser = useMutation({ | ||
mutationKey: [QUERY_KEYS.deleteFollow, userId], | ||
mutationFn: () => deleteFollowUser(userId), | ||
onSuccess: () => { | ||
queryClient.invalidateQueries({ | ||
queryKey: [QUERY_KEYS.userOne, userId], | ||
}); | ||
}, | ||
}); | ||
|
||
const handleFollowUser = () => { | ||
// 1. follow 하는 api 요청 + update | ||
const handleFollowUser = (isFollowed: boolean) => () => { | ||
if (isFollowed) { | ||
deleteFollowingUser.mutate(); | ||
} else { | ||
if (userMeData && userMeData?.followingCount >= MAX_FOLLOWING) { | ||
toasting({ type: 'warning', txt: toastMessage.ko.limitFollow }); | ||
return; | ||
} | ||
followUser.mutate(); | ||
} | ||
}; | ||
|
||
return ( | ||
<button className={styles.button} onClick={handleFollowUser}> | ||
{label} | ||
<button | ||
className={`${isFollowed ? styles.variant.gray : styles.variant.primary}`} | ||
onClick={handleFollowUser(isFollowed)} | ||
> | ||
{isFollowed ? '팔로우 취소' : '팔로우'} | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* 숫자 형태를 단위에 맞게 변환해주는 포매팅 함수입니다. | ||
- 만 미만: 축약 없이 컴마(,) 처리 ex. 1~9,999 (ko, en) | ||
- 만 이상: 만 단위 ex. 1만, 35.5만, 510만... (ko) | ||
- 만 이상: K 단위(소숫점 1자리) ex. 10K, 355.3K (en) | ||
- 백만 이상: M 단위(소숫점 1자리) ex. 5.1M (en) | ||
* @param {number} num 축약할 숫자입니다. | ||
* @param {'ko' | 'en'} lang 적용할 숫자 국가 단위입니다. | ||
* @returns {number} 단위에 맞게 변환된 숫자입니다. | ||
*/ | ||
|
||
const numberFormatter = (num: number, lang: 'ko' | 'en') => { | ||
const unit = 10000; | ||
|
||
if (num / unit < 1) { | ||
return num.toLocaleString('ko-KR'); | ||
} | ||
|
||
if (lang === 'ko') { | ||
const formattedNumKo = Math.trunc((num / unit) * 10) / 10; | ||
return formattedNumKo + '만'; | ||
} else { | ||
const formattedNumEn = Math.trunc((num / unit) * 10); | ||
if (formattedNumEn < 1000) { | ||
return formattedNumEn + 'K'; | ||
} else { | ||
const formattedMillion = Math.trunc((formattedNumEn / 1000) * 10) / 10; | ||
return formattedMillion + 'M'; | ||
} | ||
} | ||
}; | ||
|
||
export default numberFormatter; |