Skip to content

Commit 4bdacba

Browse files
committed
feat(users): 무한 스크롤 적용'
;
1 parent 9e07174 commit 4bdacba

15 files changed

Lines changed: 510 additions & 152 deletions

File tree

src/features/auction/auction-like/api/use-auction-like.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
1+
"use client";
2+
3+
import { useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query";
24

35
import type { UserAuctionLikeItemType, UserTradeStatusType } from "@/entities/auction";
46
import { httpClient } from "@/shared/api/client";
7+
import type { SliceResponseType } from "@/shared/api/types/response";
58
import { API_ENDPOINTS } from "@/shared/config/endpoints";
69
import {
710
getAuctionsCacheSnapshot,
@@ -44,14 +47,26 @@ const mapStatusToTradeStatus = (status: string): UserTradeStatusType => {
4447
}
4548
};
4649

47-
const fetchAuctionLike = async (): Promise<UserAuctionLikeItemType[]> => {
48-
const response = await httpClient<LikeData>(API_ENDPOINTS.myLikes, {
50+
const fetchAuctionLike = async (
51+
pageParam: number
52+
): Promise<SliceResponseType<UserAuctionLikeItemType>> => {
53+
const response = await httpClient<LikeData>(`${API_ENDPOINTS.myLikes}?page=${pageParam}&size=5`, {
4954
method: "GET",
5055
});
5156

52-
const slice = response.data?.slice ?? [];
57+
const { data } = response;
58+
59+
if (!data) {
60+
return {
61+
slice: [],
62+
hasNext: false,
63+
page: pageParam,
64+
size: 10,
65+
timeStamp: "",
66+
};
67+
}
5368

54-
return slice
69+
const mappedSlice = data.slice
5570
.filter((item): item is LikeSliceItem => item != null)
5671
.map((item) => {
5772
const price =
@@ -70,6 +85,11 @@ const fetchAuctionLike = async (): Promise<UserAuctionLikeItemType[]> => {
7085
date: item.startedAt,
7186
};
7287
});
88+
89+
return {
90+
...data,
91+
slice: mappedSlice,
92+
};
7393
};
7494

7595
const removeLike = async (auctionId: number) => {
@@ -83,9 +103,11 @@ export const userAuctionLikeKeys = {
83103
};
84104

85105
export function useUserAuctionLike() {
86-
return useQuery({
106+
return useInfiniteQuery({
87107
queryKey: userAuctionLikeKeys.all,
88-
queryFn: fetchAuctionLike,
108+
queryFn: ({ pageParam = 0 }) => fetchAuctionLike(pageParam),
109+
initialPageParam: 0,
110+
getNextPageParam: (lastPage) => (lastPage.hasNext ? lastPage.page + 1 : undefined),
89111
});
90112
}
91113

src/features/auction/auction-sale/api/use-sales.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
1+
import { useInfiniteQuery, useQueryClient, useMutation } from "@tanstack/react-query";
22
import dayjs from "dayjs";
33

44
import type { UserSellingItemType, UserTradeStatusType } from "@/entities/auction";
55
import { httpClient } from "@/shared/api/client";
6+
import type { SliceResponseType } from "@/shared/api/types/response";
67
import { API_ENDPOINTS } from "@/shared/config/endpoints";
78
import { showToast } from "@/shared/lib/utils/toast/show-toast";
89

@@ -40,12 +41,28 @@ const mapStatusToTradeStatus = (status: string): UserTradeStatusType => {
4041
}
4142
};
4243

43-
const getUserSalesList = async (userId: number): Promise<UserSellingItemType[]> => {
44-
const response = await httpClient<SalesData>(API_ENDPOINTS.userSales(userId), { method: "GET" });
44+
const getUserSalesList = async (
45+
userId: number,
46+
pageParam: number
47+
): Promise<SliceResponseType<UserSellingItemType>> => {
48+
const response = await httpClient<SalesData>(
49+
`${API_ENDPOINTS.userSales(userId)}?page=${pageParam}&size=5`,
50+
{ method: "GET" }
51+
);
4552

46-
const slice = response.data?.slice ?? [];
53+
const { data } = response;
4754

48-
return slice
55+
if (!data) {
56+
return {
57+
slice: [],
58+
hasNext: false,
59+
page: pageParam,
60+
size: 10,
61+
timeStamp: "",
62+
} as SliceResponseType<UserSellingItemType>;
63+
}
64+
65+
const mappedSlice = data.slice
4966
.filter((item): item is SalesSliceItem => item !== null)
5067
.map((item) => {
5168
const price =
@@ -66,6 +83,11 @@ const getUserSalesList = async (userId: number): Promise<UserSellingItemType[]>
6683
unreadMessageCount: item.chatInfo?.unreadCount,
6784
};
6885
});
86+
87+
return {
88+
...data,
89+
slice: mappedSlice,
90+
};
6991
};
7092

7193
const cancelAuction = async (auctionId: number) => {
@@ -78,9 +100,11 @@ export const userSaleKeys = {
78100
};
79101

80102
export function useUserSalesList(userId: number) {
81-
return useQuery({
103+
return useInfiniteQuery({
82104
queryKey: userSaleKeys.list(userId),
83-
queryFn: () => getUserSalesList(userId),
105+
queryFn: ({ pageParam = 0 }) => getUserSalesList(userId, pageParam),
106+
initialPageParam: 0,
107+
getNextPageParam: (lastPage) => (lastPage.hasNext ? lastPage.page + 1 : undefined),
84108
enabled: !!userId && userId > 0,
85109
staleTime: 1000 * 60 * 5,
86110
});

src/features/notification-preference/api/use-notification.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
1+
"use client";
2+
3+
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
24

35
import type { UserTradeStatusType } from "@/entities/auction";
46
import { httpClient } from "@/shared/api/client";
7+
import type { SliceResponseType } from "@/shared/api/types/response";
58
import { API_ENDPOINTS } from "@/shared/config/endpoints";
69
import {
710
getAuctionsCacheSnapshot,
@@ -83,13 +86,27 @@ const generateKeywords = (info: NotificationInfo): string[] => {
8386
return keywords;
8487
};
8588

86-
const fetchNotifications = async (): Promise<NotificationPreferenceItemType[]> => {
87-
const response = await httpClient<NotificationData>(API_ENDPOINTS.myNotifications, {
88-
method: "GET",
89-
});
90-
const slice = response.data?.slice ?? [];
89+
const fetchNotifications = async (
90+
pageParam: number
91+
): Promise<SliceResponseType<NotificationPreferenceItemType>> => {
92+
const response = await httpClient<NotificationData>(
93+
`${API_ENDPOINTS.myNotifications}?page=${pageParam}&size=5`,
94+
{ method: "GET" }
95+
);
96+
97+
const { data } = response;
9198

92-
return slice
99+
if (!data) {
100+
return {
101+
slice: [],
102+
hasNext: false,
103+
page: pageParam,
104+
size: 10,
105+
timeStamp: "",
106+
};
107+
}
108+
109+
const mappedSlice = data.slice
93110
.filter((item): item is NotificationSliceItem => item != null)
94111
.map((item) => {
95112
const finalPrice = item.endPrice || item.currentPrice || item.startPrice;
@@ -106,6 +123,11 @@ const fetchNotifications = async (): Promise<NotificationPreferenceItemType[]> =
106123
keywords: generateKeywords(item.notificationInfo),
107124
};
108125
});
126+
127+
return {
128+
...data,
129+
slice: mappedSlice,
130+
};
109131
};
110132

111133
const fetchNotificationSettings = async (
@@ -119,9 +141,11 @@ const fetchNotificationSettings = async (
119141
};
120142

121143
export function useNotificationList() {
122-
return useQuery({
144+
return useInfiniteQuery({
123145
queryKey: notificationKeys.list(),
124-
queryFn: fetchNotifications,
146+
queryFn: ({ pageParam = 0 }) => fetchNotifications(pageParam),
147+
initialPageParam: 0,
148+
getNextPageParam: (lastPage) => (lastPage.hasNext ? lastPage.page + 1 : undefined),
125149
});
126150
}
127151

src/features/purchase/api/use-purchases.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"use client";
22

3-
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3+
import { useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query";
44

55
import type { UserPurchaseItemType, UserPurchaseStatusType } from "@/entities/auction";
66
import { httpClient } from "@/shared/api/client";
7+
import type { SliceResponseType } from "@/shared/api/types/response";
78
import { API_ENDPOINTS } from "@/shared/config/endpoints";
89

910
interface PurchaseSliceItem {
@@ -34,11 +35,27 @@ interface PurchaseData {
3435
const mapPurchaseStatus = (status: PurchaseSliceItem["status"]): UserPurchaseStatusType =>
3536
status === "PURCHASE_CONFIRMED" ? "구매 확정" : "구매 완료";
3637

37-
const fetchPurchaseList = async (): Promise<UserPurchaseItemType[]> => {
38-
const res = await httpClient<PurchaseData>(API_ENDPOINTS.myPurchases, { method: "GET" });
39-
const slice = res.data?.slice ?? [];
38+
const fetchPurchaseList = async (
39+
pageParam: number
40+
): Promise<SliceResponseType<UserPurchaseItemType>> => {
41+
const res = await httpClient<PurchaseData>(
42+
`${API_ENDPOINTS.myPurchases}?page=${pageParam}&size=5`,
43+
{ method: "GET" }
44+
);
4045

41-
return slice.map((item) => ({
46+
const { data } = res;
47+
48+
if (!data) {
49+
return {
50+
slice: [],
51+
hasNext: false,
52+
page: pageParam,
53+
size: 10,
54+
timeStamp: "",
55+
};
56+
}
57+
58+
const mappedSlice = data.slice.map((item) => ({
4259
id: item.auctionId,
4360
name: item.title,
4461
imageUrl: item.auctionImageUrl,
@@ -56,6 +73,11 @@ const fetchPurchaseList = async (): Promise<UserPurchaseItemType[]> => {
5673
sellername: item.sellername,
5774
sellerProfileImage: item.sellerProfileImage,
5875
}));
76+
77+
return {
78+
...data,
79+
slice: mappedSlice,
80+
};
5981
};
6082

6183
const confirmPurchase = async (tradeId: number) => {
@@ -69,9 +91,11 @@ export const purchaseKeys = {
6991
};
7092

7193
export function usePurchases() {
72-
return useQuery({
94+
return useInfiniteQuery({
7395
queryKey: purchaseKeys.all,
74-
queryFn: fetchPurchaseList,
96+
queryFn: ({ pageParam = 0 }) => fetchPurchaseList(pageParam),
97+
initialPageParam: 0,
98+
getNextPageParam: (lastPage) => (lastPage.hasNext ? lastPage.page + 1 : undefined),
7599
staleTime: 1000 * 60 * 5,
76100
});
77101
}

src/features/recent-viewed/api/use-recent-viewed.ts

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"use client";
22

3-
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3+
import { useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query";
44

55
import type { UserRecentlyViewedItemType, UserTradeStatusType } from "@/entities/auction";
66
import { httpClient } from "@/shared/api/client";
7+
import type { SliceResponseType } from "@/shared/api/types/response";
78
import { API_ENDPOINTS } from "@/shared/config/endpoints";
89
import { dayjs } from "@/shared/lib/utils/dayjs";
910
import { showToast } from "@/shared/lib/utils/toast/show-toast";
@@ -55,36 +56,57 @@ const deleteRecentViewItem = async (recentViewId: number) => {
5556
});
5657
};
5758

59+
const fetchRecentViewedItems = async (
60+
pageParam: number
61+
): Promise<SliceResponseType<UserRecentlyViewedItemType & { recentViewId: number }>> => {
62+
const res = await httpClient<RecentViewedData>(
63+
`${API_ENDPOINTS.myRecentViews}?page=${pageParam}&size=5`,
64+
{ method: "GET" }
65+
);
66+
67+
const { data } = res;
68+
69+
if (!data) {
70+
return {
71+
slice: [],
72+
hasNext: false,
73+
page: pageParam,
74+
size: 10,
75+
timeStamp: "",
76+
};
77+
}
78+
79+
const mappedSlice = data.slice
80+
.filter((item): item is RecentViewedSliceItem => item != null)
81+
.map((item) => {
82+
const price = item.endPrice ?? item.currentPrice ?? item.startPrice ?? 0;
83+
const hasCurrentPrice = item.endPrice != null || item.currentPrice != null;
84+
85+
return {
86+
id: item.auctionId,
87+
recentViewId: item.recentViewId,
88+
name: item.title,
89+
imageUrl: item.auctionImageUrl ?? undefined,
90+
price,
91+
originalPrice: item.startPrice,
92+
discountRate: hasCurrentPrice ? (item.discountPercent ?? undefined) : undefined,
93+
status: mapStatusToTradeStatus(item.status),
94+
date: dayjs(item.startedAt).format("YYYY-MM-DD"),
95+
};
96+
});
97+
98+
return {
99+
...data,
100+
slice: mappedSlice,
101+
};
102+
};
103+
58104
export function useRecentViewedItems() {
59-
return useQuery({
105+
return useInfiniteQuery({
60106
queryKey: userRecentViewedKeys.all,
61-
queryFn: async (): Promise<(UserRecentlyViewedItemType & { recentViewId: number })[]> => {
62-
const res = await httpClient<RecentViewedData>(API_ENDPOINTS.myRecentViews, {
63-
method: "GET",
64-
});
65-
66-
const slice = res.data?.slice ?? [];
67-
68-
return slice
69-
.filter((item): item is RecentViewedSliceItem => item != null)
70-
.map((item) => {
71-
const price = item.endPrice ?? item.currentPrice ?? item.startPrice ?? 0;
72-
73-
const hasCurrentPrice = item.endPrice != null || item.currentPrice != null;
74-
75-
return {
76-
id: item.auctionId,
77-
recentViewId: item.recentViewId,
78-
name: item.title,
79-
imageUrl: item.auctionImageUrl ?? undefined,
80-
price,
81-
originalPrice: item.startPrice,
82-
discountRate: hasCurrentPrice ? (item.discountPercent ?? undefined) : undefined,
83-
status: mapStatusToTradeStatus(item.status),
84-
date: dayjs(item.startedAt).format("YYYY-MM-DD"),
85-
};
86-
});
87-
},
107+
queryFn: ({ pageParam = 0 }) => fetchRecentViewedItems(pageParam),
108+
initialPageParam: 0,
109+
getNextPageParam: (lastPage) => (lastPage.hasNext ? lastPage.page + 1 : undefined),
88110
staleTime: 1000 * 60 * 5,
89111
});
90112
}

0 commit comments

Comments
 (0)