Skip to content

Commit 5bde256

Browse files
committed
feat: infinite scroll in ui
1 parent bb8eb0a commit 5bde256

File tree

5 files changed

+105
-17
lines changed

5 files changed

+105
-17
lines changed

packages/wallet-ui/src/components/ui/molecule/List/List.style.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const List = styled.ul`
1414
padding-inline-start: 0px;
1515
display: flex;
1616
flex-direction: column;
17+
height: 100vh;
1718
`;
1819

1920
export const ListItem = styled.li``;

packages/wallet-ui/src/components/ui/molecule/TransactionsList/TransactionsList.view.tsx

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useAppSelector } from 'hooks/redux';
2-
import { FC, useEffect, useRef } from 'react';
2+
import { FC, useEffect, useRef, useState, useCallback } from 'react';
33
import { useStarkNetSnap } from 'services';
44
import { Transaction } from 'types';
55
import {
@@ -16,6 +16,9 @@ interface Props {
1616

1717
export const TransactionsListView = ({ transactions }: Props) => {
1818
const { getTransactions } = useStarkNetSnap();
19+
const seenCursors = useRef<Set<{ txnHash: string; blockNumber: number }>>(
20+
new Set(),
21+
);
1922
const networks = useAppSelector((state) => state.networks);
2023
const currentAccount = useAppSelector((state) => state.wallet.currentAccount);
2124
const erc20TokenBalanceSelected = useAppSelector(
@@ -24,9 +27,68 @@ export const TransactionsListView = ({ transactions }: Props) => {
2427
const walletTransactions = useAppSelector(
2528
(state) => state.wallet.transactions,
2629
);
30+
const walletTransactionsCursor = useAppSelector(
31+
(state) => state.wallet.transactionCursor,
32+
);
2733
const timeoutHandle = useRef(setTimeout(() => {}));
2834
const chainId = networks.items[networks.activeNetwork]?.chainId;
2935

36+
const [isFetchingMore, setIsFetchingMore] = useState(false);
37+
38+
const fetchMore = useCallback(async () => {
39+
if (
40+
walletTransactionsCursor &&
41+
!seenCursors.current.has({
42+
txnHash: walletTransactionsCursor.txnHash,
43+
blockNumber: walletTransactionsCursor.blockNumber,
44+
})
45+
) {
46+
seenCursors.current.add({
47+
txnHash: walletTransactionsCursor.txnHash,
48+
blockNumber: walletTransactionsCursor.blockNumber,
49+
});
50+
setIsFetchingMore(true);
51+
52+
await getTransactions(
53+
currentAccount.address,
54+
erc20TokenBalanceSelected.address,
55+
chainId,
56+
false,
57+
walletTransactionsCursor,
58+
);
59+
60+
setIsFetchingMore(false);
61+
}
62+
}, [
63+
walletTransactionsCursor,
64+
getTransactions,
65+
currentAccount.address,
66+
erc20TokenBalanceSelected.address,
67+
chainId,
68+
]);
69+
70+
const checkIfShouldFetchMore = (
71+
scrollTop: number,
72+
scrollHeight: number,
73+
clientHeight: number,
74+
) => {
75+
return scrollTop + clientHeight >= scrollHeight - 10;
76+
};
77+
78+
const handleScroll = useCallback(
79+
async (event: React.UIEvent<HTMLDivElement>) => {
80+
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
81+
82+
if (
83+
checkIfShouldFetchMore(scrollTop, scrollHeight, clientHeight) &&
84+
!isFetchingMore
85+
) {
86+
await fetchMore();
87+
}
88+
},
89+
[isFetchingMore, fetchMore],
90+
);
91+
3092
useEffect(() => {
3193
if (chainId && erc20TokenBalanceSelected.address) {
3294
clearTimeout(timeoutHandle.current); // cancel the timeout that was in-flight
@@ -35,10 +97,8 @@ export const TransactionsListView = ({ transactions }: Props) => {
3597
getTransactions(
3698
currentAccount.address,
3799
erc20TokenBalanceSelected.address,
38-
10,
39100
chainId,
40101
false,
41-
true,
42102
),
43103
TRANSACTIONS_REFRESH_FREQUENCY,
44104
);
@@ -58,27 +118,23 @@ export const TransactionsListView = ({ transactions }: Props) => {
58118
getTransactions(
59119
currentAccount.address,
60120
erc20TokenBalanceSelected.address,
61-
10,
62121
chainId,
63122
);
64123
}
65124
},
66125
// eslint-disable-next-line react-hooks/exhaustive-deps
67126
[
68-
// eslint-disable-next-line react-hooks/exhaustive-deps
69127
erc20TokenBalanceSelected.address,
70-
// eslint-disable-next-line react-hooks/exhaustive-deps
71128
erc20TokenBalanceSelected.chainId,
72-
// eslint-disable-next-line react-hooks/exhaustive-deps
73129
currentAccount.address,
74-
// eslint-disable-next-line react-hooks/exhaustive-deps
75130
currentAccount.chainId,
76131
chainId,
77132
],
78133
);
79134

80135
return (
81136
<Wrapper<FC<IListProps<Transaction>>>
137+
onScroll={handleScroll}
82138
data={transactions.length > 0 ? transactions : walletTransactions}
83139
render={(transaction) => (
84140
<TransactionListItem transaction={transaction} />

packages/wallet-ui/src/components/ui/organism/Header/SendSummaryModal/SendSummaryModal.view.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,8 @@ export const SendSummaryModalView = ({
157157
getTransactions(
158158
currentAddress,
159159
erc20TokenBalanceSelected.address,
160-
10,
161160
chainId,
162161
false,
163-
true,
164162
).catch((err) => {
165163
console.error(
166164
`handleConfirmClick: error from getTransactions: ${err}`,

packages/wallet-ui/src/services/useStarkNetSnap.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
updateAccount,
3030
updateCurrentAccount,
3131
setWalletConnection,
32+
setTransactionCursor,
33+
appendTransactions,
3234
} from 'slices/walletSlice';
3335
import { setNetworksAndActiveNetwork } from 'slices/networkSlice';
3436
import { disableLoading, enableLoadingWithMessage } from 'slices/UISlice';
@@ -60,6 +62,9 @@ export const useStarkNetSnap = () => {
6062
(state) => state.wallet.erc20TokenBalances,
6163
);
6264
const accounts = useAppSelector((state) => state.wallet.accounts);
65+
const transactionDeploy = useAppSelector(
66+
(state) => state.wallet.transactionDeploy,
67+
);
6368

6469
const connectToSnap = async () => {
6570
dispatch(enableLoadingWithMessage('Connecting...'));
@@ -236,6 +241,7 @@ export const useStarkNetSnap = () => {
236241

237242
const setErc20TokenBalance = (erc20TokenBalance: Erc20TokenBalance) => {
238243
dispatch(setErc20TokenBalanceSelected(erc20TokenBalance));
244+
dispatch(setTransactionDeploy(null));
239245
};
240246

241247
const initTokensAndBalances = async (chainId: string, address: string) => {
@@ -443,38 +449,51 @@ export const useStarkNetSnap = () => {
443449
const getTransactions = async (
444450
senderAddress: string,
445451
contractAddress: string,
446-
txnsInLastNumOfDays: number,
447452
chainId: string,
448453
showLoading: boolean = true,
449-
onlyFromState: boolean = false,
454+
cursor?: { blockNumber: number; txnHash: string },
450455
) => {
456+
if (transactionDeploy) {
457+
return;
458+
}
451459
if (showLoading) {
452460
dispatch(enableLoadingWithMessage('Retrieving transactions...'));
453461
}
454462

455463
try {
456-
const data = await invokeSnap<Array<Transaction>>({
464+
console.log(cursor);
465+
const response = await invokeSnap<{
466+
transactions: Transaction[];
467+
cursor: { txnHash: string; blockNumber: number };
468+
}>({
457469
method: 'starkNet_getTransactions',
458470
params: {
459471
senderAddress,
460472
contractAddress,
461-
txnsInLastNumOfDays,
473+
cursor,
462474
chainId,
463475
},
464476
});
465477

478+
const data = response.transactions;
479+
const newCursor = response.cursor;
480+
481+
console.log(newCursor);
466482
let storedTxns = data;
467483

484+
if (cursor) {
485+
dispatch(appendTransactions(storedTxns));
486+
} else {
487+
dispatch(setTransactions(storedTxns));
488+
}
468489
//Set the deploy transaction
469490
const deployTransaction = storedTxns.find(
470491
(txn: Transaction) =>
471492
txn.txnType === TransactionType.DEPLOY ||
472493
txn.txnType === TransactionType.DEPLOY_ACCOUNT,
473494
);
474495
dispatch(setTransactionDeploy(deployTransaction));
475-
476-
dispatch(setTransactions(storedTxns));
477-
496+
dispatch(setTransactionCursor(newCursor));
478497
return data;
479498
} catch (error) {
480499
dispatch(setTransactions([]));

packages/wallet-ui/src/slices/walletSlice.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export interface WalletState {
1818
erc20TokenBalances: Erc20TokenBalance[];
1919
erc20TokenBalanceSelected: Erc20TokenBalance;
2020
transactions: Transaction[];
21+
transactionCursor?: {
22+
blockNumber: number;
23+
txnHash: string;
24+
};
2125
transactionDeploy?: Transaction;
2226
provider?: any; //TODO: metamask SDK is not export types
2327
visibility: {
@@ -36,6 +40,7 @@ const initialState: WalletState = {
3640
erc20TokenBalances: [],
3741
erc20TokenBalanceSelected: {} as Erc20TokenBalance,
3842
transactions: [],
43+
transactionCursor: undefined,
3944
transactionDeploy: undefined,
4045
provider: undefined,
4146
visibility: {
@@ -155,9 +160,16 @@ export const walletSlice = createSlice({
155160
setErc20TokenBalanceSelected: (state, { payload }) => {
156161
state.erc20TokenBalanceSelected = payload;
157162
},
163+
appendTransactions: (state, { payload }) => {
164+
const transactions = state.transactions.concat(payload);
165+
state.transactions = transactions;
166+
},
158167
setTransactions: (state, { payload }) => {
159168
state.transactions = payload;
160169
},
170+
setTransactionCursor: (state, { payload }) => {
171+
state.transactionCursor = payload;
172+
},
161173
setTransactionDeploy: (state, { payload }) => {
162174
state.transactionDeploy = payload;
163175
},
@@ -187,7 +199,9 @@ export const {
187199
setErc20TokenBalances,
188200
setErc20TokenBalanceSelected,
189201
upsertErc20TokenBalance,
202+
appendTransactions,
190203
setTransactions,
204+
setTransactionCursor,
191205
setTransactionDeploy,
192206
resetWallet,
193207
setProvider,

0 commit comments

Comments
 (0)