Skip to content

Commit 28ada08

Browse files
authored
[GSW-2406] Optimize portfolio page initial rendering with data slicing pattern (#789)
* feat: [GSW-2406] optimize portfolio page initial rendering with data slicing * refactor: [GSW-2406] Apply Common Breakpoints * refactor: [GSW-2406] improve readability with explicit boolean rendering conditions * refactor: [GSW-2406] simplify import path for positionCardListBreakPoints
1 parent d64a254 commit 28ada08

File tree

5 files changed

+138
-76
lines changed

5 files changed

+138
-76
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
interface PositionCardListBreakPoint {
2+
width: number;
3+
displayCount: number;
4+
}
5+
6+
export const positionCardListBreakPoints: PositionCardListBreakPoint[] = [
7+
{ width: 1180, displayCount: 4 },
8+
{ width: 920, displayCount: 3 },
9+
];

packages/web/src/common/values/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "./storage-constant";
44
export * from "./style-constant";
55
export * from "./text-constant";
66
export * from "./launchpad-constant";
7+
export * from "./breakpoint.constant";
Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useMemo } from "react";
22

33
import LoadMoreButton from "@components/common/load-more-button/LoadMoreButton";
44
import { pulseSkeletonStyle } from "@constants/skeleton.constant";
@@ -46,48 +46,59 @@ const MyPositionCardList: React.FC<MyPositionCardListProps> = ({
4646
themeKey,
4747
showLoadMore,
4848
tokenPrices,
49-
}) => (
50-
<CardListWrapper $loading={isLoading}>
51-
<HorizontalScrollWrapper ref={divRef} onScroll={onScroll} loading={isLoading}>
52-
{!isLoading &&
53-
positions.length > 0 &&
54-
positions.map((position, idx) => (
55-
<MyPositionCard
56-
address={address}
57-
tokenPrices={tokenPrices}
58-
currentIndex={idx}
59-
position={position}
60-
key={idx}
61-
movePoolDetail={movePoolDetail}
62-
mobile={mobile}
63-
themeKey={themeKey}
64-
/>
65-
))}
66-
{isFetched &&
67-
!isLoading &&
68-
positions.length > 0 &&
69-
positions.length < 4 &&
70-
Array((width <= 1180 && width >= 768 ? 3 : 4) - positions.length)
71-
.fill(1)
72-
.map((_, index) => <BlankPositionCard key={index} />)}
73-
{(!isFetched && positions.length === 0) || isLoading
74-
? Array.from({ length: width <= 1180 && width >= 768 ? 3 : 4 }).map((_, idx) => (
49+
}) => {
50+
const hasPositions = positions.length > 0;
51+
const shouldShowSkeleton = isLoading || (!isFetched && !hasPositions);
52+
const shouldShowPositions = !isLoading && hasPositions;
53+
const shouldShowBlankCards = isFetched && !isLoading && hasPositions && positions.length < 4;
54+
const shouldShowLoadMoreButton = !mobile && !isLoading && showLoadMore && !!onClickLoadMore;
55+
const shouldShowPagination = showPagination && isFetched && hasPositions && !isLoading;
56+
57+
const maxDisplayCount = useMemo(() => {
58+
return width <= 1180 && width >= 768 ? 3 : 4;
59+
}, [width]);
60+
61+
const blankCardCount = useMemo(() => {
62+
if (!shouldShowBlankCards) return 0;
63+
return maxDisplayCount - positions.length;
64+
}, [shouldShowBlankCards, maxDisplayCount, positions.length]);
65+
66+
return (
67+
<CardListWrapper $loading={isLoading}>
68+
<HorizontalScrollWrapper ref={divRef} onScroll={onScroll} loading={isLoading}>
69+
{shouldShowPositions &&
70+
positions.map((position, idx) => (
71+
<MyPositionCard
72+
address={address}
73+
tokenPrices={tokenPrices}
74+
currentIndex={idx}
75+
position={position}
76+
key={idx}
77+
movePoolDetail={movePoolDetail}
78+
mobile={mobile}
79+
themeKey={themeKey}
80+
/>
81+
))}
82+
{shouldShowBlankCards &&
83+
Array(blankCardCount)
84+
.fill(1)
85+
.map((_, index) => <BlankPositionCard key={index} />)}
86+
{shouldShowSkeleton &&
87+
Array.from({ length: maxDisplayCount }).map((_, idx) => (
7588
<span key={idx} className="card-skeleton" css={pulseSkeletonStyle({ w: "100%", tone: "600" })} />
76-
))
77-
: null}
78-
</HorizontalScrollWrapper>
79-
{!mobile && !isLoading && showLoadMore && onClickLoadMore && (
80-
<LoadMoreButton show={loadMore} onClick={onClickLoadMore} />
81-
)}
89+
))}
90+
</HorizontalScrollWrapper>
91+
{shouldShowLoadMoreButton && <LoadMoreButton show={loadMore} onClick={onClickLoadMore} />}
8292

83-
{showPagination && isFetched && positions.length !== 0 && !isLoading && (
84-
<div className="box-indicator">
85-
<span className="current-page">{currentIndex}</span>
86-
<span>/</span>
87-
<span>{positions.length}</span>
88-
</div>
89-
)}
90-
</CardListWrapper>
91-
);
93+
{shouldShowPagination && (
94+
<div className="box-indicator">
95+
<span className="current-page">{currentIndex}</span>
96+
<span>/</span>
97+
<span>{positions.length}</span>
98+
</div>
99+
)}
100+
</CardListWrapper>
101+
);
102+
};
92103

93104
export default MyPositionCardList;

packages/web/src/layouts/earn/containers/earn-my-position-container/EarnMyPositionContainer.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useWallet } from "@hooks/wallet/data/use-wallet";
1313
import { useGetUsernameByAddress } from "@query/address";
1414
import { EarnState, ThemeState } from "@states/index";
1515
import { PoolPositionModel } from "@models/position/pool-position-model";
16+
import { positionCardListBreakPoints } from "@common/values";
1617

1718
import EarnMyPositions from "../../components/earn-my-positions/EarnMyPositions";
1819
import { PositionConverter } from "@services/converters/position";
@@ -233,12 +234,7 @@ const EarnMyPositionContainer: React.FC<EarnMyPositionContainerProps> = ({
233234
return showedPosition;
234235
}
235236

236-
const breakpoints = [
237-
{ width: 1180, displayCount: 4 },
238-
{ width: 920, displayCount: 3 },
239-
];
240-
241-
for (const breakpoint of breakpoints) {
237+
for (const breakpoint of positionCardListBreakPoints) {
242238
if (width > breakpoint.width) {
243239
return showedPosition.slice(0, breakpoint.displayCount);
244240
}
@@ -269,6 +265,10 @@ const EarnMyPositionContainer: React.FC<EarnMyPositionContainerProps> = ({
269265
}, Number(pools?.[0]?.totalApr ?? 0));
270266
}, [pools]);
271267

268+
const showLoadMore = useMemo(() => {
269+
return showedPosition.length > 4;
270+
}, [showedPosition]);
271+
272272
return (
273273
<EarnMyPositions
274274
address={address}
@@ -292,7 +292,7 @@ const EarnMyPositionContainer: React.FC<EarnMyPositionContainerProps> = ({
292292
divRef={divRef}
293293
currentIndex={currentIndex}
294294
showPagination={showPagination}
295-
showLoadMore={showedPosition.length > 4}
295+
showLoadMore={showLoadMore}
296296
width={width}
297297
loadMore={!isViewMorePositions}
298298
onClickLoadMore={handleClickLoadMore}

packages/web/src/layouts/portfolio/containers/wallet-position-card-list-container/WalletPositionCardListContainer.tsx

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { PositionMapper } from "@models/position/mapper/position-mapper";
1313
import { PoolPositionModel } from "@models/position/pool-position-model";
1414
import { ThemeState } from "@states/index";
1515
import { PositionConverter } from "@services/converters/position";
16+
import { positionCardListBreakPoints } from "@common/values";
1617

1718
const WalletPositionCardListContainer: React.FC<{ isClosed: boolean }> = ({ isClosed }) => {
1819
const { getGnotPath } = useGnotToGnot();
@@ -35,6 +36,14 @@ const WalletPositionCardListContainer: React.FC<{ isClosed: boolean }> = ({ isCl
3536
const divRef = useRef<HTMLDivElement | null>(null);
3637
const { tokenPrices = {} } = useTokenData();
3738

39+
const [isViewMorePositions, setIsViewMorePositions] = useState(false);
40+
const [mappedData, setMappedData] = useState<PoolPositionModel[]>([]);
41+
const [isDataMappingLoading, setIsDataMappingLoading] = useState(true);
42+
43+
const handleClickLoadMore = useCallback(() => {
44+
setIsViewMorePositions(!isViewMorePositions);
45+
}, [isViewMorePositions]);
46+
3847
const handleResize = () => {
3948
if (typeof window !== "undefined") {
4049
if (window.innerWidth < 920) setMobile(true);
@@ -64,22 +73,8 @@ const WalletPositionCardListContainer: React.FC<{ isClosed: boolean }> = ({ isCl
6473
}
6574
}, [positionsData, width]);
6675

67-
const handleScroll = () => {
68-
if (divRef.current) {
69-
const container = divRef.current;
70-
const currentScrollX = container.scrollLeft;
71-
const maxScroll = container.scrollWidth - container.clientWidth;
72-
73-
if (currentScrollX >= maxScroll - 1) {
74-
setCurrentIndex(positions.length);
75-
} else {
76-
setCurrentIndex(Math.min(Math.floor(currentScrollX / 322) + 1, positions.length));
77-
}
78-
}
79-
};
80-
81-
const positions = useMemo(() => {
82-
const poolPositions: PoolPositionModel[] = [];
76+
const poolPositions = useMemo(() => {
77+
const mappedPositions: PoolPositionModel[] = [];
8378
positionsData.forEach(position => {
8479
const pool = pools.find(pool => pool.poolPath === position.poolPath);
8580
if (pool) {
@@ -96,43 +91,89 @@ const WalletPositionCardListContainer: React.FC<{ isClosed: boolean }> = ({ isCl
9691
logoURI: getGnotPath(pool.tokenB).logoURI,
9792
},
9893
};
99-
poolPositions.push(PositionMapper.makePoolPosition(position, temp));
94+
mappedPositions.push(PositionMapper.makePoolPosition(position, temp));
10095
}
10196
});
10297

103-
return PositionConverter.convertPositions(poolPositions);
104-
}, [pools, positionsData]);
98+
return mappedPositions;
99+
}, [pools, positionsData, getGnotPath]);
105100

106101
const openPosition = useMemo(() => {
107-
return positions
102+
return poolPositions
108103
.filter(item => !item.closed)
109104
.sort((x, y) => Number(y.positionUsdValue) - Number(x.positionUsdValue));
110-
}, [positions]);
105+
}, [poolPositions]);
111106

112107
const closedPosition = useMemo(() => {
113-
return positions.filter(item => item.closed);
114-
}, [positions]);
108+
return poolPositions.filter(item => item.closed);
109+
}, [poolPositions]);
115110

116111
const showedPosition = useMemo(() => {
117112
return [...openPosition, ...(isClosed ? closedPosition : [])];
118113
}, [closedPosition, isClosed, openPosition]);
119114

115+
const handleScroll = useCallback(() => {
116+
if (divRef.current) {
117+
const container = divRef.current;
118+
const currentScrollX = container.scrollLeft;
119+
const maxScroll = container.scrollWidth - container.clientWidth;
120+
121+
if (currentScrollX >= maxScroll - 1) {
122+
setCurrentIndex(showedPosition.length);
123+
} else {
124+
setCurrentIndex(Math.min(Math.floor(currentScrollX / 322) + 1, showedPosition.length));
125+
}
126+
}
127+
}, [showedPosition.length]);
128+
129+
const getMappedData = (): PoolPositionModel[] => {
130+
if (isViewMorePositions) {
131+
return showedPosition;
132+
}
133+
134+
for (const breakpoint of positionCardListBreakPoints) {
135+
if (width > breakpoint.width) {
136+
return showedPosition.slice(0, breakpoint.displayCount);
137+
}
138+
}
139+
140+
return showedPosition;
141+
};
142+
143+
const updateDataMapping = useCallback(() => {
144+
setIsDataMappingLoading(true);
145+
const newMappedData = getMappedData();
146+
const convertedMappedData = PositionConverter.convertPositions(newMappedData);
147+
148+
setMappedData(convertedMappedData);
149+
setIsDataMappingLoading(false);
150+
}, [isViewMorePositions, width, showedPosition]);
151+
152+
useEffect(() => {
153+
updateDataMapping();
154+
}, [updateDataMapping]);
155+
156+
const showLoadMore = useMemo(() => {
157+
return showedPosition.length > 4;
158+
}, [showedPosition]);
159+
120160
return (
121161
<MyPositionCardList
122-
positions={showedPosition}
123-
loadMore={false}
162+
positions={mappedData}
163+
loadMore={!isViewMorePositions}
124164
isFetched={isFetchedPosition}
125-
isLoading={loading || isLoadingPosition}
165+
isLoading={loading || isLoadingPosition || isDataMappingLoading}
126166
movePoolDetail={movePoolDetail}
127167
currentIndex={currentIndex}
128168
mobile={mobile}
129169
width={width}
130170
showPagination={showPagination}
131-
showLoadMore={true}
171+
showLoadMore={showLoadMore}
132172
themeKey={themeKey}
133173
divRef={divRef}
134174
onScroll={handleScroll}
135175
tokenPrices={tokenPrices}
176+
onClickLoadMore={handleClickLoadMore}
136177
/>
137178
);
138179
};

0 commit comments

Comments
 (0)