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

Feat: 마이리스트, 콜라보리스트 페이지 팔로우 기능 구현 #26

Merged
merged 19 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a777db2
Feat: 마이리스트, 콜라보리스트 페이지 - 프로필 팔로우 기능 구현
ParkSohyunee Feb 12, 2024
a3954e4
Design: 팔로우 상태에따른 버튼 UI 스타일 적용
ParkSohyunee Feb 12, 2024
62a3968
Feat: 로그인 상태가 아닐때 에러 처리 추가
ParkSohyunee Feb 12, 2024
279bb40
Feat: 마이리스트, 콜라보리스트 페이지 - 프로필 팔로우 취소 기능 구현
ParkSohyunee Feb 12, 2024
f796bb8
Feat: 최대 팔로잉 제한 조건 적용
ParkSohyunee Feb 12, 2024
e8d9b71
Fix: 마이리스트, 콜라보리스트 페이지 관련 api export문 default로 수정
ParkSohyunee Feb 12, 2024
77d5e48
Feat: 프로필 팔로잉, 팔로우 수 천 단위 콤마 적용
ParkSohyunee Feb 12, 2024
e0baf4d
Style: 주석정리, console.log 정리
ParkSohyunee Feb 12, 2024
4bf31f4
Fix: 리스트 아이템 타입 프로퍼티 이름 수정
ParkSohyunee Feb 12, 2024
68edd47
Feat: 프로필 정보 불러오기 enabled 옵션 추가, 토스트 메세지 추가
ParkSohyunee Feb 14, 2024
3152de5
Design: 토스트 메세지 수정
ParkSohyunee Feb 14, 2024
699f147
Merge branch 'dev' into feature/feed-follow
ParkSohyunee Feb 14, 2024
d3e9463
Style: 공백 수정
ParkSohyunee Feb 14, 2024
e86d9f8
Fix: dev 브랜치 merge에 따른 리스트 상세조회 타입 수정
ParkSohyunee Feb 14, 2024
0d8b8c5
Feat: 토스트메세지 상수화 처리
ParkSohyunee Feb 14, 2024
4d7a098
Feat: 팔로우 요청 제한 로직 수정 및 토스트 메세지 수정
ParkSohyunee Feb 14, 2024
a2ba72d
Feat: 숫자를 1만 단위로 축약하는 number formatter 로직 구현
ParkSohyunee Feb 14, 2024
6b13628
Feat: 숫자를 미국 K단위, M단위로 축약하는 number formatter 로직 구현
ParkSohyunee Feb 14, 2024
5fd79a4
Style: 숫자 포맷팅 함수에 jsdoc 주석 추가
ParkSohyunee Feb 15, 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
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 @@ -34,6 +34,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,
},
],
});
80 changes: 68 additions & 12 deletions src/app/user/[userId]/_components/FollowButton.tsx
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],
});
},
Comment on lines +38 to +42
Copy link
Contributor

Choose a reason for hiding this comment

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

이렇게 다시 불러올 수 있는거군요...!
와 저 프로필 설정에서 '저장' 버튼 누른다음에 다시 fetch 해올때 딱 이 방법이 필요했어요! 지금은 수정사항 발생시 true로 바뀌었던 'isDirty'를 '저장' 버튼을 누르면 false로 바꾸는 식으로 구현했거든요! 당장 코드 바꾸러 갑니다 🙇‍♀️

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>
);
}
8 changes: 4 additions & 4 deletions src/app/user/[userId]/_components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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>
Copy link
Contributor

Choose a reason for hiding this comment

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

소현님, 저희가 숫자의 경우 축약형을 사용하기로 했는데, 공용정책 중 '숫자'를 IA에만 적어두고 피그마로 안 옮겼네요! 소현님 덕분에 발견하여, 급하게 추가했습니다!

Screenshot 2024-02-14 at 19 51 47

팔로워도 축약형으로 나타내고, hover시 정확한 숫자 나오게 하거나 '팔로잉/팔로워' 목록에서 정확한 숫자 나오게 하는 식은 어떨까요??

+) toLocaleString('ko-KR') 얻어갑니다 👍🙇‍♀️

Copy link
Contributor Author

@ParkSohyunee ParkSohyunee Feb 14, 2024

Choose a reason for hiding this comment

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

그렇네요 서영님!! 정책대로 축약형으로 나타내는 것으로 수정하겠습니다! (+.공통 유틸로 분리해 놓겠습니다.)

+. 생각해보니 천이상 ~ 만, 십만, 백만까지가 축약이 많이된 것 같아서 다음과 같이 변경해도 괜찮을 것 같은데.. 서영님 생각은 어떠신가요?

  • 백 이하 : 그대로 ~ 999
  • 1만 이하: 그대로 1,000 ~ 9,999
  • 만 이상: 축양형 10k, 10.1k 10.8k 15k ~ (버림x)
  • 백만 이상: 축약형 1M, 1.1M ~ (버림x)

Copy link
Contributor

@seoyoung-min seoyoung-min Feb 14, 2024

Choose a reason for hiding this comment

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

[KO]
천 이하: 0~9,999
1만 이상: 1만, 11.1만, 510만, ~ (소숫점 한 자리까지 노출 - 천 단위의 버림)

[ENG]
천 이하: 0~9,999
1만 이상: 10.5k, 111k, / 5.1M, ~ (소숫점 한 자리까지 노출)

K: 1,000
M:1,000,000

<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',
};
7 changes: 4 additions & 3 deletions src/lib/types/listType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export interface ListDetailType {
collectCount: number;
viewCount: number;
}

// 리스트 전체 조회 타입
export interface AllListType {
cursorId: number;
Expand All @@ -98,9 +99,9 @@ export interface ListType {

export interface ItemType {
id: number;
ranking: number;
rank: number;
title: string;
comment?: string;
link?: string;
comment?: string; // 리스트 상세조회 타입에서 사용할때는 optional 여부 확인하기
link?: string; // 리스트 상세조회 타입에서 사용할때는 optional 여부 확인하기
imageUrl?: string;
}