Skip to content

Commit

Permalink
Fix: dev와의 충돌 해결
Browse files Browse the repository at this point in the history
  • Loading branch information
seoyoung-min committed Feb 15, 2024
2 parents 0f71c85 + ca45024 commit ad7f2d1
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import * as styles from './Footer.css';
import CollectIcon from '/public/icons/collect.svg';
import ShareIcon from '/public/icons/share.svg';
import EtcIcon from '/public/icons/etc.svg';
import { CollaboratorType, ListItemsType } from '@/lib/types/listType';
import { ItemType } from '@/lib/types/listType';
import { UserProfileType } from '@/lib/types/userProfileType';

interface BottomSheetOptionsProps {
key: string;
Expand All @@ -29,8 +30,8 @@ interface FooterProps {
listId: string;
title: string;
description: string;
items: ListItemsType[];
collaborators: CollaboratorType[];
items: ItemType[];
collaborators: UserProfileType[];
ownerNickname: string;
}

Expand Down
7 changes: 7 additions & 0 deletions src/app/_api/follow/createFollowUser.ts
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;
7 changes: 7 additions & 0 deletions src/app/_api/follow/deleteFollowUser.ts
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;
7 changes: 4 additions & 3 deletions src/app/_api/list/getAllList.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axiosInstance from '@/lib/axios/axiosInstance';
import { AllListType } from '@/lib/types/listType';

export async function getAllList(userId: number, type: string, category: string, cursorId?: number) {
const getAllList = async (userId: number, type: string, category: string, cursorId?: number) => {
const params = new URLSearchParams({
type,
category,
Expand All @@ -13,6 +13,7 @@ export async function getAllList(userId: number, type: string, category: string,
}

const response = await axiosInstance.get<AllListType>(`/users/${userId}/lists?${params.toString()}`);

return response.data;
}
};

export default getAllList;
4 changes: 3 additions & 1 deletion src/app/_api/user/getUserOne.ts
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;
1 change: 1 addition & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default function TempLayout({ children }: { children: ReactNode }) {
<QueryClientProvider client={queryClient}>
<div id="modal-root" />
<div>{children}</div>
<ToastContainer />
</QueryClientProvider>
</body>
</html>
Expand Down
2 changes: 1 addition & 1 deletion src/app/user/[userId]/_components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function Card({ list, isOwner }: CardProps) {
{list.listItems.map((item) => (
<li key={item.id} className={styles.item}>
<span className={styles.rank}>
{item.ranking}
{item.rank}
{'.'}
</span>
<span className={styles.itemTitle}>{item.title}</span>
Expand Down
4 changes: 2 additions & 2 deletions src/app/user/[userId]/_components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import BlueLineLongIcon from '/public/icons/blue_line_long.svg';
import Card from './Card';
import Categories from './Categories';

import { getUserOne } from '@/app/_api/user/getUserOne';
import { getAllList } from '@/app/_api/list/getAllList';
import getUserOne from '@/app/_api/user/getUserOne';
import getAllList from '@/app/_api/list/getAllList';

import { QUERY_KEYS } from '@/lib/constants/queryKeys';
import { UserType } from '@/lib/types/userProfileType';
Expand Down
25 changes: 19 additions & 6 deletions src/app/user/[userId]/_components/FollowButton.css.ts
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,
},
],
});
78 changes: 66 additions & 12 deletions src/app/user/[userId]/_components/FollowButton.tsx
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>
);
}
13 changes: 9 additions & 4 deletions src/app/user/[userId]/_components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ 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';
import numberFormatter from '@/lib/utils/numberFormatter';

export default function Profile({ userId }: { userId: number }) {
const [hasError, setHasError] = useState(false);
Expand Down Expand Up @@ -78,15 +79,19 @@ 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 !== undefined && numberFormatter(data.followingCount, 'ko')}
</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 !== undefined && numberFormatter(data.followerCount, 'ko')}
</span>
<span>팔로워</span>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/lib/constants/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ export const QUERY_KEYS = {
getRecommendedLists: 'getRecommendedLists',
getRecommendedUsers: 'getRecommendedUsers',
getTrendingLists: 'getTrendingLists',
follow: 'follow',
deleteFollow: 'deleteFollow',
};
6 changes: 6 additions & 0 deletions src/lib/constants/toastMessage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
export const MAX_FOLLOWING = 1000;

const toastMessage = {
ko: {
requiredLogin: '로그인이 필요해요.',
limitFollow: `최대 ${MAX_FOLLOWING.toLocaleString('ko-KR')}명까지 팔로우할 수 있어요.`,
uploadImageError: '이미지를 업로드에 실패했어요. 다시 업로드해주세요.🥲',
createListError: '리스트 생성에 실패했어요. 다시 시도해주세요.🥲',
updateProfileSuccess: '프로필을 수정했습니다.🥰',
updateProfileError: '프로필 수정에 실패했어요. 다시 시도해주세요.🥲',
imageSizeError: '사진이 너무 커요. 50MB 이하 사진을 넣어주세요.🥹',
},
en: {
requiredLogin: 'Login is required.',
limitFollow: `Following exceeds the limit of ${MAX_FOLLOWING.toLocaleString('en-US')}.`,
uploadImageError: 'Failed to upload the image. Please try again.🥲',
createListError: 'Failed to create the list. Please try again.🥲',
updateProfileSuccess: 'Profile updated successfully.🥰',
Expand Down
34 changes: 34 additions & 0 deletions src/lib/utils/numberFormatter.ts
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;

0 comments on commit ad7f2d1

Please sign in to comment.