Skip to content

Commit 9f6cd11

Browse files
Infinite Scroll for BTC Transactions
1 parent 5921133 commit 9f6cd11

File tree

2 files changed

+88
-12
lines changed

2 files changed

+88
-12
lines changed

apps/frontend/src/components/cln/BTCTransactionsList/BTCTransactionsList.tsx

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import './BTCTransactionsList.scss';
2-
import { useState } from 'react';
2+
import { useCallback, useEffect, useRef, useState } from 'react';
33
import { motion, AnimatePresence } from 'framer-motion';
44
import { Spinner, Alert, Row, Col } from 'react-bootstrap';
5+
import PerfectScrollbar from 'react-perfect-scrollbar';
56

67
import { formatCurrency, titleCase } from '../../../utilities/data-formatters';
78
import { IncomingArrowSVG } from '../../../svgs/IncomingArrow';
89
import { OutgoingArrowSVG } from '../../../svgs/OutgoingArrow';
910
import DateBox from '../../shared/DateBox/DateBox';
1011
import FiatBox from '../../shared/FiatBox/FiatBox';
1112
import Transaction from '../BTCTransaction/BTCTransaction';
12-
import { TRANSITION_DURATION, Units } from '../../../utilities/constants';
13+
import { SCROLL_BATCH_SIZE, SCROLL_THRESHOLD, TRANSITION_DURATION, Units } from '../../../utilities/constants';
1314
import { NoBTCTransactionDarkSVG } from '../../../svgs/NoBTCTransactionDark';
1415
import { NoBTCTransactionLightSVG } from '../../../svgs/NoBTCTransactionLight';
1516
import { useSelector } from 'react-redux';
@@ -178,6 +179,69 @@ export const BTCTransactionsList = () => {
178179
const initExpansions = (listBitcoinTransactions.btcTransactions?.reduce((acc: boolean[]) => [...acc, false], []) || []);
179180
const [expanded, setExpanded] = useState<boolean[]>(initExpansions);
180181

182+
const [displayedTransactions, setDisplayedTransactions] = useState<any[]>([]);
183+
const [currentIndex, setCurrentIndex] = useState(0);
184+
const [isLoading, setIsLoading] = useState(false);
185+
const [allTransactionsLoaded, setAllTransactionsLoaded] = useState(false);
186+
const containerRef = useRef<HTMLDivElement>(null);
187+
188+
const setContainerRef = useCallback((ref: HTMLElement | null) => {
189+
if (ref) {
190+
(containerRef as React.MutableRefObject<HTMLElement | null>).current = ref;
191+
}
192+
}, []);
193+
194+
useEffect(() => {
195+
if (listBitcoinTransactions?.btcTransactions?.length > 0) {
196+
const initialBatch = listBitcoinTransactions?.btcTransactions.slice(0, SCROLL_BATCH_SIZE);
197+
console.warn('Initial batch of transactions:', initialBatch);
198+
setDisplayedTransactions(initialBatch);
199+
setCurrentIndex(SCROLL_BATCH_SIZE);
200+
if (SCROLL_BATCH_SIZE >= listBitcoinTransactions?.btcTransactions.length) {
201+
setAllTransactionsLoaded(true);
202+
}
203+
}
204+
}, [listBitcoinTransactions]);
205+
206+
const loadMoreTransactions = useCallback(() => {
207+
if (isLoading || allTransactionsLoaded) return;
208+
setIsLoading(true);
209+
setTimeout(() => {
210+
const nextIndex = currentIndex + SCROLL_BATCH_SIZE;
211+
const newTransactions = listBitcoinTransactions?.btcTransactions.slice(
212+
currentIndex,
213+
nextIndex
214+
);
215+
setDisplayedTransactions(prev => [...prev, ...newTransactions]);
216+
setCurrentIndex(nextIndex);
217+
218+
if (nextIndex >= listBitcoinTransactions?.btcTransactions.length) {
219+
setAllTransactionsLoaded(true);
220+
}
221+
222+
setIsLoading(false);
223+
}, 300);
224+
}, [currentIndex, isLoading, allTransactionsLoaded, listBitcoinTransactions]);
225+
226+
const handleScroll = useCallback((container) => {
227+
if (!container || isLoading || allTransactionsLoaded) return;
228+
229+
const { scrollTop, scrollHeight, clientHeight } = container;
230+
const bottomOffset = scrollHeight - scrollTop - clientHeight;
231+
232+
if (bottomOffset < SCROLL_THRESHOLD) {
233+
loadMoreTransactions();
234+
}
235+
}, [isLoading, allTransactionsLoaded, loadMoreTransactions]);
236+
237+
useEffect(() => {
238+
const container = containerRef.current;
239+
if (container) {
240+
container?.addEventListener('scroll', handleScroll);
241+
return () => container?.removeEventListener('scroll', handleScroll);
242+
}
243+
}, [handleScroll]);
244+
181245
return (
182246
isAuthenticated && listBitcoinTransactions.isLoading ?
183247
<span className='h-100 d-flex justify-content-center align-items-center'>
@@ -187,13 +251,28 @@ export const BTCTransactionsList = () => {
187251
listBitcoinTransactions.error ?
188252
<Alert className='py-0 px-1 fs-7' variant='danger'>{listBitcoinTransactions.error}</Alert> :
189253
listBitcoinTransactions?.btcTransactions && listBitcoinTransactions?.btcTransactions.length && listBitcoinTransactions?.btcTransactions.length > 0 ?
190-
<div className='btc-transactions-list' data-testid='btc-transactions-list'>
191-
{
192-
listBitcoinTransactions?.btcTransactions?.map((transaction, i) => (
193-
<BTCTransactionsAccordion key={i} i={i} expanded={expanded} setExpanded={setExpanded} initExpansions={initExpansions} transaction={transaction} />
194-
))
254+
<PerfectScrollbar
255+
containerRef={setContainerRef}
256+
onScrollY={handleScroll}
257+
className='btc-transactions-list'
258+
data-testid='btc-transactions-list'
259+
options={{
260+
suppressScrollX: true,
261+
wheelPropagation: false
262+
}}
263+
>
264+
{displayedTransactions.map((transaction, i) => (
265+
<BTCTransactionsAccordion key={i} i={i} expanded={expanded} setExpanded={setExpanded} initExpansions={initExpansions} transaction={transaction} />
266+
))}
267+
{isLoading && (
268+
<Col xs={12} className='d-flex align-items-center justify-content-center mb-5'>
269+
<Spinner animation='grow' variant='primary' />
270+
</Col>
271+
)}
272+
{allTransactionsLoaded && listBitcoinTransactions?.btcTransactions.length > 100 &&
273+
<h6 className='d-flex align-self-center py-4 text-muted'>No more transactions to load!</h6>
195274
}
196-
</div>
275+
</PerfectScrollbar>
197276
:
198277
<Row className='text-light fs-6 h-75 mt-2 align-items-center justify-content-center'>
199278
<Row className='d-flex align-items-center justify-content-center'>

apps/frontend/src/components/cln/BTCWallet/BTCWallet.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import './BTCWallet.scss';
22
import { lazy, Suspense } from 'react';
3-
import PerfectScrollbar from 'react-perfect-scrollbar';
43
import { Spinner, Alert, Card, Col, ButtonGroup } from 'react-bootstrap';
54
const BTCTransactionsList = lazy(() => import('../BTCTransactionsList/BTCTransactionsList'));
65
import { BitcoinWalletSVG } from '../../../svgs/BitcoinWallet';
@@ -55,9 +54,7 @@ const BTCWallet = (props) => {
5554
<Card.Body className="px-4 list-scroll-container">
5655
<div className="text-light btc-transactions-tabs">Transactions</div>
5756
<Suspense fallback={<Loading />}>
58-
<PerfectScrollbar>
59-
<BTCTransactionsList />
60-
</PerfectScrollbar>
57+
<BTCTransactionsList />
6158
</Suspense>
6259
</Card.Body>
6360
</Card.Body>

0 commit comments

Comments
 (0)