From 2de86c80a8e3e0d5ac8ebed6cc24d34950f28695 Mon Sep 17 00:00:00 2001 From: malik1004x Date: Sat, 14 Mar 2026 14:31:50 +0100 Subject: [PATCH] improve transaction list performance (#3082) * improve transaction list performance * fix "no transactions" placeholder positioning * format --- assets/new-ui/lightning-icon.svg.vec | Bin 0 -> 2802 bytes lib/new-ui/pages/home_page.dart | 15 ++-- .../assets_history_section.dart | 4 +- .../assets_history/assets_section.dart | 72 +++++++++--------- .../assets_history/assets_top_bar.dart | 14 ++-- .../assets_history/history_section.dart | 45 ++++++----- .../assets_history/history_tile.dart | 2 +- .../dashboard/pages/nft_listing_page.dart | 62 +++++++-------- .../dashboard/dashboard_view_model.dart | 28 ++++--- .../dashboard/transaction_list_item.dart | 8 ++ res/pictures/lightning-icon.svg | 47 ++++++++++++ 11 files changed, 186 insertions(+), 111 deletions(-) create mode 100644 assets/new-ui/lightning-icon.svg.vec create mode 100644 res/pictures/lightning-icon.svg diff --git a/assets/new-ui/lightning-icon.svg.vec b/assets/new-ui/lightning-icon.svg.vec new file mode 100644 index 0000000000000000000000000000000000000000..09d9daa76b913dcecf7c590bb62d377b1e9e5d4c GIT binary patch literal 2802 zcmd5;YfKbZ6u!Fy!=kk+MR8e{s%<5N5)rq%)wIZ-gX~I0!4hn(;v-5IMOLCh5e?XF zs|}){u`0D_IOo16M+u{_JFDzI$iRoO{oA z&iQ8NMXn{Pa6(ofJHrSG;GP1Z2g4)umYR^@mPZ1~6$CnzkHQHgOx2gEWV^7pt-4QJ zmE}00=Vazrw`1G{LYQApw2hqAG|p=ar1YX7ytk#dU!Oj06RQM5PjSCx+dh^3`u1si z=AIzXlPIy3ZPUh(FrKU)au*0m2N4TX7!5>S_E;e44|?(7C)>uzwh5xGK#zl6-LKk) z1aEpIaD)g#2z$<{s8eaACMtQv(XB}LdOO!aOid4tsMIR@V;3%#-6f>!tAnrsa&Z2E5!k-Mfm_!vzP@3tg-kYlxe26?`|1^ zZpV3ZWyu0m{BvK}Av~l%Hm7NETG&ExW$Ur9X*M)|JVKmL?g+(uudjqd0>Rs#--F2} z<12WqE~aN*;37CXV;^)LNXBO?Y9QY*9V6D1!>0FB#JTqHM_BakCAeN1fNWiF?5;}e znX~=qDCs;SLL?}djffJv!iB<(5{nX>5~~us!iB<(!j;0E!lmM0N7FozEvRNWq3NL) znx*mL9w?vTl+S_NE6m}G&zn`n65q_HYk@u1+h-ijkK$8*W;v#w48bY8=A$b)4Lfio zexh~YQfC`zD|7IXobSG3VC!x72}WCWf zNh^33-Duk$HylfLzA%3~cP?JP)e1{(*%*MsaawaBu9`g;!;)uUdU`gF(FEi1#zL&l zZ-A{+W?;(w!)9e2or)vxuz*=r(lNb=BX$*MtZErMY@WspSx4s69BRbevK)Hn)H~M` zhjxg$mTEzR%6sc3eo621_;p>Uh}TNadf5e_=k+z^8O zXf%?a3$T0$PyJi~r57qHha&{qVT|@ojONopvG*K~7T3=cd%%L^w+85ZX2GWA_26!s zglSvrAfh+{D}Ql9TtPxlPk8qT>lvkoSg*03^6oM39#q!TQ3-mDC4lBaObD&nQFQwM z?QP$G*{@30B9OJnBx@1K{HOHfMbaXg7g3WgfW(tD?@qY^5|0*HixOomQg}DZS|ru0 zQnw6;RWmJ8>R3g!h$m5uUy)u$GCO5ipvoZj2Gc3=NzV0ML#h8n8diTJ=(4#$W7b4=ZVS&+J3gGVE6(6UT}n`);)yUP#Bt;H~InM&?e ziF3Bk>d*9e^GrPaIZTgR2E{|QE(DjHjDr*Fg7Hp;74Ev10okd>jYXMoBGU&ySf2r< z#lA@DZD1Y2qivTB^1^vkm)Rg}43F1KZBT6DF=ku_Bqj2=dBH;1XQR)9Godq|#|>{R zhO6K3*#2ZOU4zGaJF=jB1CKd(vfxSykM<}#*r+DlPPK!JV$?$R-QY1gSHhbsB~&yc zcR(GD53o>E2Q%vdySX}OYyfPy?u4X0aX4!L#mIPEeYpm%L|Jg&?-bn@JlRzP9h?>O z{GHG^){6Z{IRWNSjX@_ITV%!ViB1}?757YXLTZ*3Yv(&5D9?%o`<-xql@+6dY9Ym8 z#l(hMAca<3z1anXzF#tDH#CP@apbul!6)aXR-L?SO)_dtH2iE`_KmOqSmbEOCLWkz&m zd!XEI#G*_OT$*9T=NTR-NHF43n}>df*mwJ-7M^}xkHnq?cLVgON=$;{Ae#4xByexk zWB;Eg!>%el&YYS6dwe5c2^ezkypg&sXdOHZ9dg|fyFgisazV2hxZfMcMh c$|QoX1Uv(d{u83$Lo^J0i9Z9Lu)jrr13n`6tpET3 literal 0 HcmV?d00001 diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 8da84dc50..c4fedb630 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -144,18 +144,21 @@ class _NewHomePageState extends State { ); }, ), + + ], + ), + ), Observer( builder: (_) => AssetsHistorySection( nftViewModel: widget.nftViewModel, dashboardViewModel: widget.dashboardViewModel, ), ), - SizedBox(height: 80.0) - ], - ), - ),] - ), - Container( + SliverToBoxAdapter( + child: SizedBox(height: 80.0), + ) + ]), + Container( height: (MediaQuery.of(context).padding.top), decoration: BoxDecoration( gradient: LinearGradient( diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart index 9eb7bc649..998ebc137 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart @@ -68,8 +68,8 @@ class _AssetsHistorySectionState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ + return SliverMainAxisGroup( + slivers: [ // if(tabs.length>1) AssetsTopBar( dashboardViewModel: widget.dashboardViewModel, diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart index e2d15eea8..74990e294 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart @@ -15,45 +15,47 @@ class AssetsSection extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 64.0), - child: Observer( - builder: (context) { - final hasMweb = dashboardViewModel.hasMweb && dashboardViewModel.mwebEnabled; - return ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - physics: NeverScrollableScrollPhysics(), - itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length + (hasMweb ? 1 : 0), - itemBuilder: (context, index) { - return Observer(builder: (context) { - if (hasMweb) { + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(bottom: 64.0), + child: Observer( + builder: (context) { + final hasMweb = dashboardViewModel.hasMweb && dashboardViewModel.mwebEnabled; + return ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + physics: NeverScrollableScrollPhysics(), + itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length + (hasMweb ? 1 : 0), + itemBuilder: (context, index) { + return Observer(builder: (context) { + if (hasMweb) { + return AssetTile( + showSwap: dashboardViewModel.isEnabledSwapAction, + balance: dashboardViewModel.balanceViewModel.formattedBalances.first, + showSecondary: index > 0 ? true : false, + wallet: dashboardViewModel.wallet, + chainIconPath: index > 0 ? dashboardViewModel.wallet.currency.chainIconPath! : "", + trailingText: index > 0 ? "MWEB" : null, + modalMode: index > 0 + ? AssetDetailsModalModes.ltcPrivate + : AssetDetailsModalModes.ltcTransparent, + title: index > 0 ? "Litecoin Private" : null, + ); + } + + final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); return AssetTile( showSwap: dashboardViewModel.isEnabledSwapAction, - balance: dashboardViewModel.balanceViewModel.formattedBalances.first, - showSecondary: index > 0 ? true : false, + balance: balance, wallet: dashboardViewModel.wallet, - chainIconPath: index > 0 ? dashboardViewModel.wallet.currency.chainIconPath! : "", - trailingText: index > 0 ? "MWEB" : null, - modalMode: index > 0 - ? AssetDetailsModalModes.ltcPrivate - : AssetDetailsModalModes.ltcTransparent, - title: index > 0 ? "Litecoin Private" : null, + chainIconPath: _getChainIconPath(), ); - } - - final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); - return AssetTile( - showSwap: dashboardViewModel.isEnabledSwapAction, - balance: balance, - wallet: dashboardViewModel.wallet, - chainIconPath: _getChainIconPath(), - ); - }); - - }, - ); - } + }); + + }, + ); + } + ), ), ); } diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart index bec40bfd1..c2a19f5fb 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart @@ -26,10 +26,10 @@ class AssetsTopBar extends StatelessWidget { final settingsButtonText = _getSettingsButtonText(); final hasTokenSettingsButton = settingsButtonText != null; - - return Padding( - padding: const EdgeInsets.only(top: 12.0, bottom: 12.0, left: 12.0, right: 18.0), - child: Row( + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(top: 12.0, bottom: 12.0, left: 12.0, right: 18.0), + child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -82,9 +82,9 @@ class AssetsTopBar extends StatelessWidget { ), ), ), - ), - - ], + ), + ], + ), ), ); } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_section.dart b/lib/new-ui/widgets/coins_page/assets_history/history_section.dart index fd1617fec..8ee960838 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_section.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_section.dart @@ -27,26 +27,29 @@ class HistorySection extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Observer( - builder: (_) => (dashboardViewModel.items.isEmpty && - dashboardViewModel.status is! SyncingSyncStatus) - ? Padding( - padding: EdgeInsets.only(top: 24), - child: Text(S.of(context).transactions_will_appear_here, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Theme.of(context).colorScheme.onSurfaceVariant))) - : ListView.builder( - physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: dashboardViewModel.items.length, - itemBuilder: (context, index) => Observer(builder: (_) { - final prevItem = index == 0 ? null : dashboardViewModel.items[index - 1]; - final item = dashboardViewModel.items[index]; + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + sliver: Observer( + builder: (_) => (dashboardViewModel.items.isEmpty && + dashboardViewModel.status is! SyncingSyncStatus) + ? SliverPadding( + padding: EdgeInsets.only(top: 24), + sliver: SliverToBoxAdapter( + child: Center( + child: Text(S.of(context).transactions_will_appear_here, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurfaceVariant)), + ), + ), + ) + : SliverList( + delegate: SliverChildBuilderDelegate( + childCount: dashboardViewModel.items.length, + (context, index) => Observer(builder: (_) { + final prevItem = index == 0 ? null : dashboardViewModel.items[index - 1]; + final item = dashboardViewModel.items[index]; final nextItem = index == dashboardViewModel.items.length - 1 ? null : dashboardViewModel.items[index + 1]; @@ -163,6 +166,6 @@ class HistorySection extends StatelessWidget { }), ), ), - ); + )); } } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart b/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart index ef4ed615a..3fcbc8dd8 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart @@ -63,7 +63,7 @@ class HistoryTile extends StatelessWidget { return Stack( children: [ CakeImageWidget(imageUrl: - "assets/images/lightning-icon.svg", + "assets/new-ui/lightning-icon.svg", width: 34, height: 34, ), diff --git a/lib/src/screens/dashboard/pages/nft_listing_page.dart b/lib/src/screens/dashboard/pages/nft_listing_page.dart index c59300860..616c7bdfc 100644 --- a/lib/src/screens/dashboard/pages/nft_listing_page.dart +++ b/lib/src/screens/dashboard/pages/nft_listing_page.dart @@ -31,38 +31,40 @@ class _NFTListingPageState extends State { @override Widget build(BuildContext context) { - return Observer( - builder: (context) { - return Column( - children: [ - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), - child: PrimaryButton( - text: S.current.import, - color: Theme.of(context).colorScheme.surfaceContainer, - textColor: Theme.of(context).colorScheme.onSecondaryContainer, - onPressed: () => Navigator.pushNamed( - context, - Routes.importNFTPage, - arguments: widget.nftViewModel, - ), - ), - ), - if (widget.nftViewModel.isLoading) - Center( - child: CircularProgressIndicator( - backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, - valueColor: AlwaysStoppedAnimation( - Theme.of(context).colorScheme.primary, + return SliverToBoxAdapter( + child: Observer( + builder: (context) { + return Column( + children: [ + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), + child: PrimaryButton( + text: S.current.import, + color: Theme.of(context).colorScheme.surfaceContainer, + textColor: Theme.of(context).colorScheme.onSecondaryContainer, + onPressed: () => Navigator.pushNamed( + context, + Routes.importNFTPage, + arguments: widget.nftViewModel, ), ), - ) - else - NFTListWidget(nftViewModel: widget.nftViewModel) - ], - ); - }, + ), + if (widget.nftViewModel.isLoading) + Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.primary, + ), + ), + ) + else + NFTListWidget(nftViewModel: widget.nftViewModel) + ], + ); + }, + ), ); } } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index b92fb5d2c..2a699e384 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -327,7 +327,7 @@ abstract class DashboardViewModelBase with Store { } catch (_) {} } return length * confirmations; - }, _transactionDisposerCallback); + }, _transactionDisposerCallback, delay: 300); if (hasSilentPayments) { silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); @@ -481,13 +481,23 @@ abstract class DashboardViewModelBase with Store { } // printV("Transaction disposer callback (relevantTxs: ${relevantTxs.length} current: ${transactions.length})"); - transactions.clear(); - transactions.addAll(relevantTxs.map((tx) => TransactionListItem( - transaction: tx, - balanceViewModel: balanceViewModel, - appStore: appStore, - key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'), - ))); + final newTransactions = relevantTxs + .map((tx) => TransactionListItem( + transaction: tx, + balanceViewModel: balanceViewModel, + appStore: appStore, + key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'), + )) + .where((item) => !transactions.contains(item)); + + transactions.addAll(newTransactions); + // transactions.clear(); + // transactions.addAll(relevantTxs.map((tx) => TransactionListItem( + // transaction: tx, + // balanceViewModel: balanceViewModel, + // appStore: appStore, + // key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'), + // ))); } finally { _isTransactionDisposerCallbackRunning = false; } @@ -1252,7 +1262,7 @@ abstract class DashboardViewModelBase with Store { } catch (_) {} } return length * confirmations; - }, _transactionDisposerCallback); + }, _transactionDisposerCallback, delay: 300); } @action diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index e765e88c5..de025e114 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -289,4 +289,12 @@ class TransactionListItem extends ActionListItem with Keyable { @override DateTime get date => transaction.date; + + @override + bool operator ==(Object other) { + if (other is TransactionListItem) { + return other.transaction.txHash == transaction.txHash; + } + return false; + } } diff --git a/res/pictures/lightning-icon.svg b/res/pictures/lightning-icon.svg new file mode 100644 index 000000000..272baa52d --- /dev/null +++ b/res/pictures/lightning-icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +