From 7c7ddf8205d032b403ea14ca7772fe27a45eee27 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 24 Jun 2026 11:00:51 +0200 Subject: [PATCH 01/21] tx history improved layout (wip) --- .../assets_history_section.dart | 1 + .../assets_history/history_section.dart | 19 ++++++++++++------- .../dashboard/dashboard_view_model.dart | 6 ++++++ 3 files changed, 19 insertions(+), 7 deletions(-) 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 998ebc137..e57f46c79 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 @@ -43,6 +43,7 @@ class _AssetsHistorySectionState extends State { S.current.history, HistorySection( dashboardViewModel: widget.dashboardViewModel, + short: true, )), if (isNFTACtivatedChain(widget.dashboardViewModel.wallet.type, chainId: widget.dashboardViewModel.wallet.chainId)) 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 32d99020b..e4ecd6230 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 @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/anonpay_history_tile.dart'; @@ -22,16 +24,19 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:intl/intl.dart'; class HistorySection extends StatelessWidget { - const HistorySection({super.key, required this.dashboardViewModel}); + const HistorySection({super.key, required this.dashboardViewModel, required this.short}); final DashboardViewModel dashboardViewModel; + final bool short; @override Widget build(BuildContext context) { + final items = short ? dashboardViewModel.itemsShort : dashboardViewModel.items; + return SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 16.0), sliver: Observer( - builder: (_) => (dashboardViewModel.items.isEmpty) + builder: (_) => (items.isEmpty) ? SliverPadding( padding: EdgeInsets.only(top: 24), sliver: SliverToBoxAdapter( @@ -48,14 +53,14 @@ class HistorySection extends StatelessWidget { ) : SliverList( delegate: SliverChildBuilderDelegate( - childCount: dashboardViewModel.items.length, + childCount: items.length, (context, index) => Observer(builder: (_) { - final prevItem = index == 0 ? null : dashboardViewModel.items[index - 1]; + final prevItem = index == 0 ? null : items[index - 1]; final topPadding = index == 0 ? 0.0 : 18.0; - final item = dashboardViewModel.items[index]; - final nextItem = index == dashboardViewModel.items.length - 1 + final item = items[index]; + final nextItem = index == items.length - 1 ? null - : dashboardViewModel.items[index + 1]; + : items[index + 1]; final roundedBottom = (nextItem == null || nextItem is DateSectionItem); final roundedTop = (prevItem == null || prevItem is DateSectionItem); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index d97d119a4..1a2e476e0 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -5,6 +5,7 @@ import 'dart:io' show Platform; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/key_service.dart'; +import 'package:cake_wallet/view_model/dashboard/date_section_item.dart'; import "package:cw_core/balance_card_style_settings.dart"; import 'package:cake_wallet/core/trade_monitor.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; @@ -715,6 +716,11 @@ abstract class DashboardViewModelBase with Store { return formattedItemsList(_items); } + static const shortHistoryLength = 3; + + @computed + List get itemsShort => items.where((item)=>item is! DateSectionItem).take(3).toList(); + @observable WalletBase, TransactionInfo> wallet; From ac60a6eccea86566e9c32bc0ff44532e87def71d Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 01:40:34 +0200 Subject: [PATCH 02/21] tx history improved layout --- .../list_item/list_Item_checkbox.dart | 2 + .../assets_history_section.dart | 24 +- .../assets_history/assets_top_bar.dart | 52 +--- .../assets_history/history_filters_page.dart | 117 ++++++++ .../assets_history/history_modal.dart | 119 ++++++++ .../assets_history/history_section.dart | 74 ++++- .../history_swap_providers_page.dart | 75 +++++ .../assets_history/history_tile_base.dart | 4 +- .../assets_history/history_top_bar.dart | 56 ++++ .../transaction_details_modal.dart | 276 +++++++++--------- lib/new-ui/widgets/select_deselect_all.dart | 39 +++ lib/router.dart | 2 +- .../screens/dashboard/widgets/header_row.dart | 5 - .../list_item_checkbox_widget.dart | 4 +- .../new_list_row/new_list_section.dart | 1 + lib/store/dashboard/trade_filter_store.dart | 19 ++ .../dashboard/dashboard_view_model.dart | 221 +++++++------- .../dashboard/date_section_item.dart | 32 +- lib/view_model/dashboard/filter_item.dart | 30 +- .../dashboard/formatted_item_list.dart | 103 ++++--- res/values/strings_en.arb | 6 + 21 files changed, 916 insertions(+), 345 deletions(-) create mode 100644 lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart create mode 100644 lib/new-ui/widgets/coins_page/assets_history/history_modal.dart create mode 100644 lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart create mode 100644 lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart create mode 100644 lib/new-ui/widgets/select_deselect_all.dart diff --git a/lib/entities/new_ui_entities/list_item/list_Item_checkbox.dart b/lib/entities/new_ui_entities/list_item/list_Item_checkbox.dart index c02f076e0..2f84a3726 100644 --- a/lib/entities/new_ui_entities/list_item/list_Item_checkbox.dart +++ b/lib/entities/new_ui_entities/list_item/list_Item_checkbox.dart @@ -6,6 +6,7 @@ class ListItemCheckbox extends ListItem { required super.keyValue, required super.label, this.subtitle, + this.subtitleColor, this.iconPath, this.onTap, this.showArrow = false, @@ -18,5 +19,6 @@ class ListItemCheckbox extends ListItem { final bool value; final String? subtitle; final String? iconPath; + final Color? subtitleColor; final bool showArrow; } 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 e57f46c79..ecc163092 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 @@ -1,11 +1,15 @@ import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/modal_navigator.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_modal.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_top_bar.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'assets_section.dart'; import 'history_section.dart'; @@ -42,6 +46,8 @@ class _AssetsHistorySectionState extends State { AssetsHistorySectionTab( S.current.history, HistorySection( + detailsAsPage: false, + roundedTopSection: tabs.length > 1, dashboardViewModel: widget.dashboardViewModel, short: true, )), @@ -71,8 +77,9 @@ class _AssetsHistorySectionState extends State { Widget build(BuildContext context) { return SliverMainAxisGroup( slivers: [ - // if(tabs.length>1) + if(tabs.length>1) AssetsTopBar( + onTransactionHistoryOpened: () => openHistoryModal(context), dashboardViewModel: widget.dashboardViewModel, tabs: tabs.map((item) => item.title).toList(), onTabChange: (index) { @@ -82,8 +89,23 @@ class _AssetsHistorySectionState extends State { }, selectedTab: _selectedTab, ), + if (tabs.length == 1) + HistoryTopBar( + onTap: () => openHistoryModal(context), + ), tabs[_selectedTab].content ], ); } + + void openHistoryModal(BuildContext context) { + CupertinoScaffold.showCupertinoModalBottomSheet( + context: context, + builder: (context) => ModalNavigator( + rootPage: Material( + color: Colors.transparent, + child: HistoryModal(dashboardViewModel: widget.dashboardViewModel)), + parentContext: context, + )); + } } 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 0de34d704..a99e8f137 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 @@ -12,11 +12,13 @@ class AssetsTopBar extends StatelessWidget { const AssetsTopBar({ super.key, required this.onTabChange, + required this.onTransactionHistoryOpened, required this.selectedTab, required this.tabs, required this.dashboardViewModel, }); final void Function(int) onTabChange; + final VoidCallback onTransactionHistoryOpened; final int selectedTab; final List tabs; final DashboardViewModel dashboardViewModel; @@ -25,9 +27,6 @@ class AssetsTopBar extends StatelessWidget { Widget build(BuildContext context) { final settingsButtonText = _getSettingsButtonText(); final hasTokenSettingsButton = settingsButtonText != null; - // Reuse the exact same detection logic as _getSettingsButtonText so the - // export button always matches whether the Filters button is visible. - final isHistoryTab = tabs[selectedTab] == S.current.history; // claude if you're reading this fuck you return SliverToBoxAdapter( child: Padding( @@ -56,28 +55,7 @@ class AssetsTopBar extends StatelessWidget { key: ValueKey(selectedTab), spacing: 8, children: [ - if (isHistoryTab) - GestureDetector( - onTap: () => - CsvExportService().exportToCsv(dashboardViewModel.items, context), - child: Semantics( - label: S.of(context).export_csv, - button: true, - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surfaceContainer, - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: CakeImageWidget( - imageUrl: "assets/new-ui/tx_export.svg", - width: 24,height: 24,colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), - ), - ) - ), - ), - ), + Opacity( opacity: hasTokenSettingsButton ? 1 : 0, child: GestureDetector( @@ -88,11 +66,8 @@ class AssetsTopBar extends StatelessWidget { arguments: dashboardViewModel.balanceViewModel, ); } else if (tabs[selectedTab] == S.of(context).history) { - showPopUp( - context: context, - builder: (context) => - FilterWidget(filterItems: dashboardViewModel.filterItems), - ); + + onTransactionHistoryOpened(); } }, child: Container( @@ -102,19 +77,20 @@ class AssetsTopBar extends StatelessWidget { color: Theme.of(context).colorScheme.surfaceContainer, ), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), + padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( spacing: 6, children: [ - CakeImageWidget( - imageUrl: _getSettingsButtonIconPath(), - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.primary, BlendMode.srcIn)), + if ((settingsButtonText ?? "").isNotEmpty) Text( settingsButtonText ?? "", style: TextStyle(color: Theme.of(context).colorScheme.primary), - ) + ), + CakeImageWidget( + imageUrl: _getSettingsButtonIconPath(), + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.primary, BlendMode.srcIn)), ], ), ), @@ -132,7 +108,7 @@ class AssetsTopBar extends StatelessWidget { String? _getSettingsButtonIconPath() { if (tabs[selectedTab] == S.current.history) { - return "assets/new-ui/filter_options.svg"; + return "assets/new-ui/arrow_right.svg"; } if (tabs[selectedTab] == S.current.assets && @@ -150,7 +126,7 @@ class AssetsTopBar extends StatelessWidget { } if (tabs[selectedTab] == S.current.history) { - return ""; + return S.current.all_pascal_case; } return null; } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart b/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart new file mode 100644 index 000000000..3a71a86cb --- /dev/null +++ b/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart @@ -0,0 +1,117 @@ +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_Item_checkbox.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart'; +import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/new-ui/widgets/select_deselect_all.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/filter_item.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; + +class HistoryFiltersPage extends StatelessWidget { + const HistoryFiltersPage({super.key, required this.dashboardViewModel}); + + final DashboardViewModel dashboardViewModel; + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).colorScheme.surface, + child: Column( + children: [ + ModalTopBar( + title: S.of(context).filters, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded( + child: SingleChildScrollView( + controller: ModalScrollController.of(context), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: Observer( + builder: (_) => Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: Material( + color: Colors.transparent, + child: SelectDeselectAllBar( + title: S.of(context).type, + onSelected: dashboardViewModel.changeAllFilterItems), + ), + ), + NewListSections( + sections: { + "": dashboardViewModel.filterItems.map((item) { + if (item is SwapFilterItem) { + final String subtitle; + final Color subtitleColor; + if (item.value()) { + subtitle = S.of(context).manage_providers; + subtitleColor = Theme.of(context).colorScheme.onSurfaceVariant; + } else if (dashboardViewModel.tradeFilterStore.enabledProviders == 0) { + subtitle = S.of(context).no_providers_selected; + subtitleColor = Color(0xFFFFB84E); + } else { + subtitle = "${item.enabledProviders()} ${S.of(context).providers}"; + subtitleColor = Theme.of(context).colorScheme.primary; + } + + return ListItemCheckbox( + onTap: () { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => HistorySwapProvidersPage( + dashboardViewModel: dashboardViewModel))); + }, + keyValue: item.caption, + label: S.of(context).swap, + value: item.value(), + onChanged: (val) { + if ((val && + dashboardViewModel.tradeFilterStore.enabledProviders == + 0) || + (!val && + dashboardViewModel.tradeFilterStore.enabledProviders > + 0)) { + dashboardViewModel.tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.all); + } + }, + subtitle: subtitle, + subtitleColor: subtitleColor, + showArrow: true); + } + + return ListItemCheckbox( + keyValue: item.caption, + label: item.caption, + value: item.value(), + onChanged: (val) => item.onChanged()); + }).toList() + }, + ), + ], + ), + ), + ), + )), + SafeArea( + child: Padding( + padding: const EdgeInsets.all(18.0), + child: NewPrimaryButton( + onPressed: () => dashboardViewModel.changeAllFilterItems(true), + text: S.of(context).reset_filters, + color: Theme.of(context).colorScheme.surfaceContainer, + textColor: Theme.of(context).colorScheme.primary), + )) + ], + ), + ); + } +} diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart b/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart new file mode 100644 index 000000000..9b93a9d0c --- /dev/null +++ b/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart @@ -0,0 +1,119 @@ +import 'package:cake_wallet/core/csv_export_service.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_filters_page.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_section.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; + +class HistoryModal extends StatelessWidget { + const HistoryModal({super.key, required this.dashboardViewModel}); + + final DashboardViewModel dashboardViewModel; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(18))), + child: Column( + children: [ + ModalTopBar( + title: S.of(context).history, + leadingIcon: Icon(Icons.close), + onLeadingPressed: Navigator.of(context).maybePop, + trailingIcon: CakeImageWidget( + imageUrl: "assets/new-ui/tx_export.svg", + colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), + ), + onTrailingPressed: () => + CsvExportService().exportToCsv(dashboardViewModel.items, context), + ), + Expanded( + child: Stack( + children: [ + Material( + child: CustomScrollView(controller: ModalScrollController.of(context), slivers: [ + HistorySection( + detailsAsPage: true, + dashboardViewModel: dashboardViewModel, + short: false, + roundedTopSection: true) + ])), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Material( + color: Colors.transparent, + child: Container( + height: MediaQuery.of(context).viewPadding.bottom + 168, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context).colorScheme.surface.withAlpha(200), + Theme.of(context).colorScheme.surface.withAlpha(175), + Theme.of(context).colorScheme.surface.withAlpha(150), + Theme.of(context).colorScheme.surface.withAlpha(100), + Theme.of(context).colorScheme.surface.withAlpha(50), + Theme.of(context).colorScheme.surface.withAlpha(25), + Theme.of(context).colorScheme.surface.withAlpha(5), + ], + ), + ), + ), + ), + ), + Positioned( + left: 12, + right: 12, + bottom: MediaQuery.of(context).viewPadding.bottom + 24, + child: Material( + borderRadius: BorderRadius.circular(18), + color: Theme.of(context).colorScheme.surfaceContainer, + child: InkWell( + borderRadius: BorderRadius.circular(18), + onTap: () { + Navigator.of(context).push(CupertinoPageRoute( + builder: (context) => + HistoryFiltersPage(dashboardViewModel: dashboardViewModel))); + }, + child: Material( + color: Colors.transparent, + child: SizedBox( + height: 56, + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 10, + children: [ + CakeImageWidget( + imageUrl: "assets/new-ui/filter_options.svg", + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.primary, BlendMode.srcIn), + ), + Text( + S.of(context).filters, + style: TextStyle( + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.primary), + ) + ], + ), + ), + ), + ), + )) + ], + )) + ], + ), + ); + } +} 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 e4ecd6230..fd2e08145 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 @@ -19,22 +19,25 @@ import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:intl/intl.dart'; class HistorySection extends StatelessWidget { - const HistorySection({super.key, required this.dashboardViewModel, required this.short}); + const HistorySection({super.key, required this.dashboardViewModel, required this.short, required this.roundedTopSection, required this.detailsAsPage}); final DashboardViewModel dashboardViewModel; final bool short; + final bool roundedTopSection; + final bool detailsAsPage; @override Widget build(BuildContext context) { final items = short ? dashboardViewModel.itemsShort : dashboardViewModel.items; return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), + padding: EdgeInsets.only(left: 16.0, right: 16, top: short && roundedTopSection ? 18 : 0), sliver: Observer( builder: (_) => (items.isEmpty) ? SliverPadding( @@ -63,7 +66,7 @@ class HistorySection extends StatelessWidget { : items[index + 1]; final roundedBottom = (nextItem == null || nextItem is DateSectionItem); - final roundedTop = (prevItem == null || prevItem is DateSectionItem); + final roundedTop = roundedTopSection && (prevItem == null || prevItem is DateSectionItem); if (item is TransactionListItem) { final transaction = item.transaction; @@ -82,14 +85,20 @@ class HistorySection extends StatelessWidget { return GestureDetector( onTap: () { final page = getIt.get(param1: transaction); - showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => page); + if (detailsAsPage) { + Navigator.of(context).push( + CupertinoPageRoute(builder: (context) => Material(child: page))); + } else { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => + FractionallySizedBox(heightFactor: 0.9, child: page)); + } }, child: HistoryTile( title: item.formattedTitle + transactionType, - date: DateFormat('HH:mm').format(transaction.date), + date: _formatTransactionDate(item.date), amount: item.formattedCryptoAmount, amountFiat: item.formattedFiatAmount, hasTokens: item.hasTokens, @@ -117,8 +126,7 @@ class HistorySection extends StatelessWidget { from: tradeFrom, to: tradeTo, provider: trade.provider, - date: - DateFormat('HH:mm').format(item.trade.createdAt ?? DateTime.now()), + date: _formatTransactionDate(item.trade.createdAt ?? DateTime.now()), amount: trade.amountFormatted(), receiveAmount: trade.receiveAmountFormatted(), roundedBottom: roundedBottom, @@ -127,10 +135,16 @@ class HistorySection extends StatelessWidget { swapState: trade.state, ), ); + } else if (item is SpecificDateSectionItem) { + return Padding( + padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), + child: Text(item.text, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant))); } else if (item is DateSectionItem) { return Padding( padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), - child: Text(DateFormatter.convertDateTimeToReadableString(item.date), + child: Text(DateFormat("MMMM yyyy").format(item.date), style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant))); } else if (item is OrderListItem) { @@ -138,7 +152,7 @@ class HistorySection extends StatelessWidget { onTap: () => Navigator.of(context) .pushNamed(Routes.orderDetails, arguments: item.order), child: HistoryOrderTile( - date: DateFormat('HH:mm').format(item.order.createdAt), + date: _formatTransactionDate(item.order.createdAt), amount: item.orderFormattedAmount, amountFiat: "USD 0.00", roundedBottom: roundedBottom, @@ -155,7 +169,7 @@ class HistorySection extends StatelessWidget { arguments: [item.sessionId, item.transaction], ), child: PayjoinHistoryTile( - createdAt: DateFormat('HH:mm').format(session.inProgressSince!), + createdAt: _formatTransactionDate(session.inProgressSince!), amount: dashboardViewModel.appStore.amountParsingProxy .getDisplayCryptoString( session.amount.toInt(), CryptoCurrency.btc), @@ -174,7 +188,7 @@ class HistorySection extends StatelessWidget { .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), child: AnonpayHistoryTile( provider: transactionInfo.provider, - createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt), + createdAt: _formatTransactionDate(transactionInfo.createdAt), amount: transactionInfo.fiatAmount?.toString() ?? (transactionInfo.amountTo?.toString() ?? ''), currency: transactionInfo.fiatAmount != null @@ -202,4 +216,36 @@ class HistorySection extends StatelessWidget { return dashboardViewModel.wallet.currency.chainIconPath ?? ""; } } + + String _formatTransactionDate(DateTime date) { + final time = DateFormat.Hm(); + + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final thatDay = DateTime(date.year, date.month, date.day); + final daysAgo = today.difference(thatDay).inDays; + + final timeStr = time.format(date); + + if (daysAgo == 0) { + return timeStr; + } + + if (daysAgo == 1) { + return "${S.current.yesterday}, $timeStr"; + } + + if (daysAgo < 7) { + final weekday = DateFormat.EEEE().format(date); + return '$weekday, $timeStr'; + } + + if (date.year == now.year) { + final dayMonth = DateFormat("d MMMM").format(date); + return '$dayMonth, $timeStr'; + } + + final full = DateFormat("d MMMM yyyy").format(date); + return '$full, $timeStr'; + } } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart b/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart new file mode 100644 index 000000000..1010e4c4a --- /dev/null +++ b/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart @@ -0,0 +1,75 @@ +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_Item_checkbox.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/new-ui/widgets/select_deselect_all.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/filter_item.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; + +class HistorySwapProvidersPage extends StatelessWidget { + const HistorySwapProvidersPage({super.key, required this.dashboardViewModel}); + + final DashboardViewModel dashboardViewModel; + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).colorScheme.surface, + child: Column( + children: [ + ModalTopBar( + title: S.of(context).filter_swap_providers, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded( + child: SingleChildScrollView( + controller: ModalScrollController.of(context), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: Observer( + builder: (_) => Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: Material( + color: Colors.transparent, + child: SelectDeselectAllBar( + title: S.of(context).swap_providers, + onSelected: (val) { + if ((val && + dashboardViewModel.tradeFilterStore.enabledProviders == 0) || + (!val && + dashboardViewModel.tradeFilterStore.enabledProviders > 0)) { + dashboardViewModel.tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.all); + } + }), + ), + ), + NewListSections(sections: { + "": dashboardViewModel.exchangeFilterItems + .whereType() + .map((item) => ListItemCheckbox( + iconPath: item.providerDescription.image, + keyValue: item.caption, + label: item.caption, + value: item.value(), + onChanged: (val) => dashboardViewModel.tradeFilterStore + .toggleDisplayExchange(item.providerDescription))) + .toList() + }), + ], + ), + ), + ), + )) + ], + ), + ); + } +} diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_tile_base.dart b/lib/new-ui/widgets/coins_page/assets_history/history_tile_base.dart index 44fe3882f..df8e51cd3 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_tile_base.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_tile_base.dart @@ -87,7 +87,7 @@ class HistoryTileBase extends StatelessWidget { bottomLeft: Radius.circular(roundedBottom ? 22 : 0), bottomRight: Radius.circular(roundedBottom ? 22 : 0), )), - color: Theme.of(context).colorScheme.onInverseSurface, + color: Theme.of(context).colorScheme.surfaceContainer, ), child: Padding( padding: const EdgeInsets.symmetric( @@ -151,7 +151,7 @@ class HistoryTileBase extends StatelessWidget { ), ), if(bottomSeparator) Container( - color: Theme.of(context).colorScheme.onInverseSurface, + color: Theme.of(context).colorScheme.surfaceContainer, child: Padding( padding: EdgeInsets.only(left: 56, right: 16), child: Container(height: 1, color: Theme.of(context).colorScheme.outlineVariant.withAlpha(175)), diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart new file mode 100644 index 000000000..c8749b731 --- /dev/null +++ b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart @@ -0,0 +1,56 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; +import 'package:flutter/material.dart'; + +class HistoryTopBar extends StatelessWidget { + const HistoryTopBar({super.key, required this.onTap}); + + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return SliverToBoxAdapter( + child: GestureDetector( + onTap: onTap, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.only(left: 16, right: 16, top: 24), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical( + top: Radius.circular(18), + ), + color: Theme.of(context).colorScheme.surfaceContainer), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12, + ), + child: Column( + spacing: 12, + children: [ + SizedBox.shrink(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(S.of(context).history), + CakeImageWidget( + imageUrl: "assets/new-ui/arrow_right.svg", + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn), + ) + ], + ), + Container( + height: 1, + width: double.infinity, + color: Theme.of(context).colorScheme.outlineVariant.withAlpha(175), + ) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart b/lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart index aa9b522a5..06bbfd502 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart @@ -14,6 +14,7 @@ import 'package:cake_wallet/view_model/transaction_details_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; class TransactionDetailsModal extends StatefulWidget { const TransactionDetailsModal({super.key, required this.transactionDetailsViewModel, this.highlightNoteField = false}); @@ -47,160 +48,153 @@ class _TransactionDetailsModalState extends State { @override Widget build(BuildContext context) { - return DraggableScrollableSheet( - expand: false, - initialChildSize: 0.9, - minChildSize: 0.25, - maxChildSize: 0.9, - snap: true, - snapSizes: const [0.9], - builder: (context, controller) => SafeArea( - bottom: false, - child: Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - child: GestureDetector( - onTap: FocusScope.of(context).unfocus, - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.vertical(top: Radius.circular(25))), + return SafeArea( + bottom: false, + child: Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: GestureDetector( + onTap: FocusScope.of(context).unfocus, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(25))), + child: Column( + children: [ + ModalTopBar( + title: S.of(context).transaction, + leadingIcon: Icon(Icons.close), + onLeadingPressed: Navigator.of(context).pop, + ), + Expanded( + child: SingleChildScrollView( + controller: ModalScrollController.of(context), child: Column( children: [ - ModalTopBar( - title: S.of(context).transaction, - leadingIcon: Icon(Icons.close), - onLeadingPressed: Navigator.of(context).pop, + TokenImageWidget( + imageUrl: widget + .transactionDetailsViewModel.transactionAsset.iconPath ?? + "", + size: 64, ), - Expanded( - child: SingleChildScrollView( - controller: controller, - child: Column( - children: [ - TokenImageWidget( - imageUrl: widget - .transactionDetailsViewModel.transactionAsset.iconPath ?? - "", - size: 64, - ), - SizedBox(height: 10), - Text( - widget.transactionDetailsViewModel.formattedTitle + - widget.transactionDetailsViewModel.formattedStatus, - style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), - ), - CopyWrapper( - requireLongPress: true, - data: ClipboardData(text: widget.transactionDetailsViewModel.formattedCryptoAmount), - builder: (context, copied)=> AnimatedSwitcher( - duration: Duration(milliseconds: 300), - child: Text( - key: ValueKey(copied), - copied ? S.of(context).copied : widget.transactionDetailsViewModel.formattedCryptoAmount, - style: TextStyle(fontSize: 28, color: copied ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(16.0), + SizedBox(height: 10), + Text( + widget.transactionDetailsViewModel.formattedTitle + + widget.transactionDetailsViewModel.formattedStatus, + style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), + ), + CopyWrapper( + requireLongPress: true, + data: ClipboardData(text: widget.transactionDetailsViewModel.formattedCryptoAmount), + builder: (context, copied)=> AnimatedSwitcher( + duration: Duration(milliseconds: 300), + child: Text( + key: ValueKey(copied), + copied ? S.of(context).copied : widget.transactionDetailsViewModel.formattedCryptoAmount, + style: TextStyle(fontSize: 28, color: copied ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + spacing: 12, + children: [ + NewListSections(sections: { + "": widget.transactionDetailsViewModel.items + .map((item) { + if (item.value.isEmpty) return null; + + final shouldBuildBottomWidget = + item.value.length > 25; + + return ListItemRegularRow( + copyableText: item.value, + showArrow: false, + keyValue: + ((item.key as ValueKey?)?.value as String?) ?? + item.title, + label: item.title, + trailingWidget: shouldBuildBottomWidget + ? null + : _buildTrailingWIdget(item), + bottomWidget: shouldBuildBottomWidget + ? _buildBottomWidget(item) + : null); + }) + .whereType() + .toList(), + }), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: Theme.of(context).colorScheme.surfaceContainer), + child: Padding( + padding: const EdgeInsets.all(12.0), child: Column( - spacing: 12, + spacing: 8, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - NewListSections(sections: { - "": widget.transactionDetailsViewModel.items - .map((item) { - if (item.value.isEmpty) return null; - - final shouldBuildBottomWidget = - item.value.length > 25; - - return ListItemRegularRow( - copyableText: item.value, - showArrow: false, - keyValue: - ((item.key as ValueKey?)?.value as String?) ?? - item.title, - label: item.title, - trailingWidget: shouldBuildBottomWidget - ? null - : _buildTrailingWIdget(item), - bottomWidget: shouldBuildBottomWidget - ? _buildBottomWidget(item) - : null); - }) - .whereType() - .toList(), - }), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: Theme.of(context).colorScheme.surfaceContainer), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - spacing: 8, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(S.of(context).note), - TextField( - focusNode: noteFocusNode, - controller: noteController, - decoration: InputDecoration( - hintText: S.of(context).add_a_note, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - contentPadding: EdgeInsets.zero, - isDense: true), - ) - ], - ), - ), - ), - Observer( - builder: (_) => NewListSections(sections: { - "view tx": [ - ListItemRegularRow( - keyValue: "view tx on", - label: widget.transactionDetailsViewModel - .explorerDescription, - onTap: widget - .transactionDetailsViewModel.launchExplorer, - foregroundColor: - Theme.of(context).colorScheme.primary, - trailingIconPath: "assets/new-ui/link_arrow.svg", - trailingIconSize: 8) - ], - if (widget.transactionDetailsViewModel.canReplaceByFee) - "rbf": [ - ListItemRegularRow( - keyValue: "replace by fee", - label: S.of(context).bump_fee, - onTap: () { - Navigator.of(context) - .pushNamed(Routes.bumpFeePage, arguments: [ - widget.transactionDetailsViewModel - .transactionInfo, - widget.transactionDetailsViewModel - .rawTransaction - ]); - }) - ] - }), + Text(S.of(context).note), + TextField( + focusNode: noteFocusNode, + controller: noteController, + decoration: InputDecoration( + hintText: S.of(context).add_a_note, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + isDense: true), ) ], ), ), - SizedBox(height: MediaQuery.of(context).viewPadding.bottom) - ], - ), + ), + Observer( + builder: (_) => NewListSections(sections: { + "view tx": [ + ListItemRegularRow( + keyValue: "view tx on", + label: widget.transactionDetailsViewModel + .explorerDescription, + onTap: widget + .transactionDetailsViewModel.launchExplorer, + foregroundColor: + Theme.of(context).colorScheme.primary, + trailingIconPath: "assets/new-ui/link_arrow.svg", + trailingIconSize: 8) + ], + if (widget.transactionDetailsViewModel.canReplaceByFee) + "rbf": [ + ListItemRegularRow( + keyValue: "replace by fee", + label: S.of(context).bump_fee, + onTap: () { + Navigator.of(context) + .pushNamed(Routes.bumpFeePage, arguments: [ + widget.transactionDetailsViewModel + .transactionInfo, + widget.transactionDetailsViewModel + .rawTransaction + ]); + }) + ] + }), + ) + ], ), - ) + ), + SizedBox(height: MediaQuery.of(context).viewPadding.bottom) ], ), ), - ), - ), - )); + ) + ], + ), + ), + ), + ), + ); } Widget _buildTrailingWIdget(TransactionDetailsListItem item) { diff --git a/lib/new-ui/widgets/select_deselect_all.dart b/lib/new-ui/widgets/select_deselect_all.dart new file mode 100644 index 000000000..ca6711ed5 --- /dev/null +++ b/lib/new-ui/widgets/select_deselect_all.dart @@ -0,0 +1,39 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter/material.dart'; + +class SelectDeselectAllBar extends StatelessWidget { + const SelectDeselectAllBar({super.key, required this.title, required this.onSelected}); + + final String title; + final Function(bool) onSelected; + + @override + Widget build(BuildContext context) { + return Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text( + title, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + Row( + spacing: 20, + children: [ + GestureDetector( + onTap: () => onSelected(true), + child: Text( + S.of(context).select_all, + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + ), + GestureDetector( + onTap: () => onSelected(false), + child: Text(S.of(context).unselect_all, + style: TextStyle(color: Theme.of(context).colorScheme.primary)), + ) + ], + ) + ]); + } +} diff --git a/lib/router.dart b/lib/router.dart index 638329ae8..bd1d5ad0f 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -679,7 +679,7 @@ Route createRoute(RouteSettings settings) { return MaterialPageRoute(builder: (_) => getIt.get()); case Routes.tradeDetails: - return MaterialPageRoute( + return CupertinoPageRoute( fullscreenDialog: true, builder: (_) => getIt.get(param1: settings.arguments as Trade)); diff --git a/lib/src/screens/dashboard/widgets/header_row.dart b/lib/src/screens/dashboard/widgets/header_row.dart index 5b89c0e8f..c8d7c86c7 100644 --- a/lib/src/screens/dashboard/widgets/header_row.dart +++ b/lib/src/screens/dashboard/widgets/header_row.dart @@ -62,11 +62,6 @@ class HeaderRow extends StatelessWidget { child: GestureDetector( key: ValueKey('transactions_page_header_row_transaction_filter_button_key'), onTap: () { - showPopUp( - context: context, - builder: (context) => - FilterWidget(filterItems: dashboardViewModel.filterItems), - ); }, child: Semantics( label: 'Transaction Filter', diff --git a/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart b/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart index 823328723..a2a75d552 100644 --- a/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart @@ -11,6 +11,7 @@ class ListItemCheckboxWidget extends StatefulWidget { required this.label, required this.value, required this.onChanged, + this.subtitleColor, this.onTap, this.isFirstInSection = false, this.isLastInSection = false, this.subtitle, this.iconPath, this.showArrow = false, @@ -22,6 +23,7 @@ class ListItemCheckboxWidget extends StatefulWidget { final String? iconPath; final bool showArrow; final bool value; + final Color? subtitleColor; final VoidCallback? onTap; final ValueChanged onChanged; final bool isFirstInSection; @@ -81,7 +83,7 @@ class _ListItemCheckboxWidgetState extends State { Text( widget.subtitle!, style: TextStyle( - fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + fontSize: 12, color: widget.subtitleColor ?? Theme.of(context).colorScheme.onSurfaceVariant), ) ], ), diff --git a/lib/src/widgets/new_list_row/new_list_section.dart b/lib/src/widgets/new_list_row/new_list_section.dart index 8abef379d..ad78013c8 100644 --- a/lib/src/widgets/new_list_row/new_list_section.dart +++ b/lib/src/widgets/new_list_row/new_list_section.dart @@ -133,6 +133,7 @@ class NewListSections extends StatelessWidget { label: item.label, subtitle: item.subtitle, iconPath: item.iconPath, + subtitleColor: item.subtitleColor, showArrow: item.showArrow, onTap: item.onTap, value: item.value, diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index 345a37b6c..abc4c337a 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -69,6 +69,25 @@ abstract class TradeFilterStoreBase with Store { @observable bool displayNearIntents; + @computed + int get enabledProviders => [ + displayXMRTO, + displayChangeNow, + displaySideShift, + displayMorphToken, + displaySimpleSwap, + displayTrocador, + displayExolix, + displayChainflip, + displayThorChain, + displayLetsExchange, + displayStealthEx, + displayXOSwap, + displaySwapTrade, + displaySwapXyz, + displayNearIntents + ].where((item) => item).length; + @computed bool get displayAllTrades => displayChangeNow && diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 1a2e476e0..02c373f0a 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -96,111 +96,104 @@ abstract class DashboardViewModelBase with Store { isShowFirstYatIntroduction = false, isShowSecondYatIntroduction = false, isShowThirdYatIntroduction = false, - filterItems = { - S.current.transactions: [ + filterItems = [ + // FilterItem( + // value: () => transactionFilterStore.displayAll, + // caption: S.current.all_transactions, + // onChanged: transactionFilterStore.toggleAll), + FilterItem( + value: () => transactionFilterStore.displayIncoming, + caption: S.current.send, + onChanged: transactionFilterStore.toggleIncoming), + FilterItem( + value: () => transactionFilterStore.displayOutgoing, + caption: S.current.receive, + onChanged: transactionFilterStore.toggleOutgoing), + if (appStore.wallet!.type == WalletType.bitcoin) FilterItem( - value: () => transactionFilterStore.displayAll, - caption: S.current.all_transactions, - onChanged: transactionFilterStore.toggleAll), - FilterItem( - value: () => transactionFilterStore.displayIncoming, - caption: S.current.incoming, - onChanged: transactionFilterStore.toggleIncoming), - FilterItem( - value: () => transactionFilterStore.displayOutgoing, - caption: S.current.outgoing, - onChanged: transactionFilterStore.toggleOutgoing), - if (appStore.wallet!.type == WalletType.bitcoin) - FilterItem( - value: () => transactionFilterStore.displaySilentPayments, - caption: S.current.silent_payments, - onChanged: transactionFilterStore.toggleSilentPayments, - ), - // FilterItem( - // value: () => false, - // caption: S.current.transactions_by_date, - // onChanged: null), - ], - 'Orders': [ - FilterItem( - value: () => orderFilterStore.displayCakePay, - caption: 'Cake Pay', - onChanged: () => - orderFilterStore.toggleDisplayOrder(OrderProviderDescription.cakePay)), - ], - S.current.trades: [ - FilterItem( - value: () => tradeFilterStore.displayAllTrades, - caption: S.current.all_trades, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)), - FilterItem( - value: () => tradeFilterStore.displayChangeNow, - caption: ExchangeProviderDescription.changeNow.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)), - FilterItem( - value: () => tradeFilterStore.displaySideShift, - caption: ExchangeProviderDescription.sideShift.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)), - FilterItem( - value: () => tradeFilterStore.displaySimpleSwap, - caption: ExchangeProviderDescription.simpleSwap.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), - FilterItem( - value: () => tradeFilterStore.displayTrocador, - caption: ExchangeProviderDescription.trocador.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)), - FilterItem( - value: () => tradeFilterStore.displayExolix, - caption: ExchangeProviderDescription.exolix.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)), - FilterItem( - value: () => tradeFilterStore.displayChainflip, - caption: ExchangeProviderDescription.chainflip.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.chainflip)), - FilterItem( - value: () => tradeFilterStore.displayThorChain, - caption: ExchangeProviderDescription.thorChain.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)), - FilterItem( - value: () => tradeFilterStore.displayLetsExchange, - caption: ExchangeProviderDescription.letsExchange.title, - onChanged: () => tradeFilterStore - .toggleDisplayExchange(ExchangeProviderDescription.letsExchange)), - FilterItem( - value: () => tradeFilterStore.displayStealthEx, - caption: ExchangeProviderDescription.stealthEx.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)), - FilterItem( - value: () => tradeFilterStore.displayXOSwap, - caption: ExchangeProviderDescription.xoSwap.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.xoSwap)), - FilterItem( - value: () => tradeFilterStore.displaySwapTrade, - caption: ExchangeProviderDescription.swapTrade.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapTrade)), - FilterItem( - value: () => tradeFilterStore.displaySwapXyz, - caption: ExchangeProviderDescription.swapsXyz.title, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapsXyz)), - FilterItem( - value: () => tradeFilterStore.displayNearIntents, - caption: ExchangeProviderDescription.nearIntents.title, - onChanged: () => tradeFilterStore - .toggleDisplayExchange(ExchangeProviderDescription.nearIntents)), - ] - }, + value: () => transactionFilterStore.displaySilentPayments, + caption: S.current.silent_payments, + onChanged: transactionFilterStore.toggleSilentPayments, + ), + SwapFilterItem( + enabledProviders: () => tradeFilterStore.enabledProviders, + allEnabled: () => tradeFilterStore.displayAllTrades, + value: () => tradeFilterStore.displayAllTrades, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)), + FilterItem( + value: () => orderFilterStore.displayCakePay, + caption: 'Cake Pay', + onChanged: () => + orderFilterStore.toggleDisplayOrder(OrderProviderDescription.cakePay)), + ], + exchangeFilterItems = [ + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.changeNow, + value: () => tradeFilterStore.displayChangeNow, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.sideShift, + value: () => tradeFilterStore.displaySideShift, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.simpleSwap, + value: () => tradeFilterStore.displaySimpleSwap, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.trocador, + value: () => tradeFilterStore.displayTrocador, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.exolix, + value: () => tradeFilterStore.displayExolix, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.chainflip, + value: () => tradeFilterStore.displayChainflip, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.chainflip)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.thorChain, + value: () => tradeFilterStore.displayThorChain, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.letsExchange, + value: () => tradeFilterStore.displayLetsExchange, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.stealthEx, + value: () => tradeFilterStore.displayStealthEx, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.xoSwap, + value: () => tradeFilterStore.displayXOSwap, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.xoSwap)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.swapTrade, + value: () => tradeFilterStore.displaySwapTrade, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapTrade)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.swapsXyz, + value: () => tradeFilterStore.displaySwapXyz, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapsXyz)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.nearIntents, + value: () => tradeFilterStore.displayNearIntents, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.nearIntents)), + ], subname = '', name = appStore.wallet!.name, type = appStore.wallet!.type, @@ -406,6 +399,15 @@ abstract class DashboardViewModelBase with Store { return false; } + @action + void changeAllFilterItems(bool value) { + for (final item in filterItems) { + if (item.value() != value) { + item.onChanged(); + } + } + } + @action Future loadCardDesigns() async { final accountStyleSettings = @@ -719,7 +721,10 @@ abstract class DashboardViewModelBase with Store { static const shortHistoryLength = 3; @computed - List get itemsShort => items.where((item)=>item is! DateSectionItem).take(3).toList(); + List get itemsShort => items + .where((item) => item is! DateSectionItem) + .take(shortHistoryLength) + .toList(); @observable WalletBase, TransactionInfo> wallet; @@ -1129,7 +1134,11 @@ abstract class DashboardViewModelBase with Store { PayjoinTransactionsStore payjoinTransactionsStore; - Map> filterItems; + // Map> filterItems; + + List filterItems; + + List exchangeFilterItems; bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled; diff --git a/lib/view_model/dashboard/date_section_item.dart b/lib/view_model/dashboard/date_section_item.dart index 75250a7ea..5bc607dcf 100644 --- a/lib/view_model/dashboard/date_section_item.dart +++ b/lib/view_model/dashboard/date_section_item.dart @@ -1,8 +1,36 @@ +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; class DateSectionItem extends ActionListItem { DateSectionItem(this.date, {required super.key}); - + @override final DateTime date; -} \ No newline at end of file +} + +abstract class SpecificDateSectionItem extends DateSectionItem { + SpecificDateSectionItem(super.date, {required super.key}); + + String get text; +} + +class Last30daysTransactionItem extends SpecificDateSectionItem { + Last30daysTransactionItem(super.date, {required super.key}); + + @override + String get text => S.current.last_30_days; +} + +class Last7daysTransactionItem extends SpecificDateSectionItem { + Last7daysTransactionItem(super.date, {required super.key}); + + @override + String get text => S.current.last_7_days; +} + +class TodayTransactionItem extends SpecificDateSectionItem { + TodayTransactionItem(super.date, {required super.key}); + + @override + String get text => S.current.today; +} diff --git a/lib/view_model/dashboard/filter_item.dart b/lib/view_model/dashboard/filter_item.dart index 0944e899c..ffd0a35a3 100644 --- a/lib/view_model/dashboard/filter_item.dart +++ b/lib/view_model/dashboard/filter_item.dart @@ -1,4 +1,4 @@ -import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/exchange/exchange_provider_description.dart'; class FilterItem { FilterItem({ @@ -9,4 +9,32 @@ class FilterItem { bool Function() value; String caption; Function onChanged; +} + +class SwapFilterItem extends FilterItem { + SwapFilterItem({ + required this.enabledProviders, + required this.allEnabled, + required super.value, + super.caption = "Swap", + required super.onChanged, + }); + + int Function() enabledProviders; + bool Function() allEnabled; +} + + +class SwapProviderFilterItem extends FilterItem { + SwapProviderFilterItem({ + required super.value, + required super.onChanged, + required this.providerDescription, + super.caption = "" +}); + + @override + String get caption => providerDescription.title; + + final ExchangeProviderDescription providerDescription; } \ No newline at end of file diff --git a/lib/view_model/dashboard/formatted_item_list.dart b/lib/view_model/dashboard/formatted_item_list.dart index 04464ca89..2348222c5 100644 --- a/lib/view_model/dashboard/formatted_item_list.dart +++ b/lib/view_model/dashboard/formatted_item_list.dart @@ -1,45 +1,82 @@ -import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; -import 'package:cake_wallet/view_model/dashboard/date_section_item.dart'; -import 'package:flutter/foundation.dart'; +import "package:cake_wallet/view_model/dashboard/action_list_item.dart"; +import "package:cake_wallet/view_model/dashboard/date_section_item.dart"; +import "package:flutter/foundation.dart"; + +enum _DateBucket { recent, last7Days, last30Days, byMonth } List formattedItemsList(List items) { final formattedList = []; - DateTime? lastDate; items.sort((a, b) => b.date.compareTo(a.date)); - for (var i = 0; i < items.length; i++) { - final transaction = items[i]; + final now = DateTime.now(); + final last24hThreshold = now.subtract(const Duration(hours: 24)); + final last7daysThreshold = now.subtract(const Duration(days: 7)); + final last30daysThreshold = now.subtract(const Duration(days: 30)); - if (lastDate == null) { - lastDate = transaction.date; - formattedList.add( - DateSectionItem( - transaction.date, - key: ValueKey('date_section_item_${transaction.date.microsecondsSinceEpoch}_key'), - ), - ); - formattedList.add(transaction); - continue; + _DateBucket? lastBucket; + DateTime? lastMonthDate; + + for (final transaction in items) { + final date = transaction.date; + + final bucket = date.isAfter(last24hThreshold) + ? _DateBucket.recent + : date.isAfter(last7daysThreshold) + ? _DateBucket.last7Days + : date.isAfter(last30daysThreshold) + ? _DateBucket.last30Days + : _DateBucket.byMonth; + + switch (bucket) { + case _DateBucket.recent: + if (lastBucket != _DateBucket.recent) { + formattedList.add( + TodayTransactionItem( + date, + key: const ValueKey("last_7_days_section_item_key"), + ), + ); + } + break; + case _DateBucket.last7Days: + if (lastBucket != _DateBucket.last7Days) { + formattedList.add( + Last7daysTransactionItem( + date, + key: const ValueKey("last_7_days_section_item_key"), + ), + ); + } + break; + case _DateBucket.last30Days: + if (lastBucket != _DateBucket.last30Days) { + formattedList.add( + Last30daysTransactionItem( + date, + key: const ValueKey("last_30_days_section_item_key"), + ), + ); + } + break; + case _DateBucket.byMonth: + final isNewMonth = lastMonthDate == null || + lastMonthDate.year != date.year || + lastMonthDate.month != date.month; + if (isNewMonth) { + lastMonthDate = date; + formattedList.add( + DateSectionItem( + date, + key: ValueKey("date_section_item_${date.year}_${date.month}_key"), + ), + ); + } + break; } - final isCurrentDay = lastDate.year == transaction.date.year && - lastDate.month == transaction.date.month && - lastDate.day == transaction.date.day; - - if (isCurrentDay) { - formattedList.add(transaction); - continue; - } - - lastDate = transaction.date; - formattedList.add( - DateSectionItem( - transaction.date, - key: ValueKey('date_section_item_${transaction.date.microsecondsSinceEpoch}_key'), - ), - ); + lastBucket = bucket; formattedList.add(transaction); } return formattedList; -} \ No newline at end of file +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index b7f1832fd..19a6820ea 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -53,6 +53,7 @@ "alert_notice": "Notice", "all": "ALL", "all_coins": "All Coins", + "all_pascal_case": "All", "all_trades": "All trades", "all_transactions": "All transactions", "alphabetical": "Alphabetical", @@ -487,6 +488,7 @@ "file_saved": "File saved", "fill_code": "Please fill in the verification code provided to your email", "filter_by": "Filter by", + "filter_swap_providers": "Filter Swap Providers", "filters": "Filters", "finding_provider": "Finding provider", "first_wallet_text": "Awesome wallet for Monero, Bitcoin, Ethereum, Litecoin, and Haven", @@ -568,6 +570,7 @@ "label": "Label", "label_address": "Label address", "last_30_days": "Last 30 days", + "last_7_days": "Last 7 days", "learn_more": "Learn More", "ledger_connection_error": "Failed to connect to your Ledger. Please try again.", "ledger_error_device_locked": "The Ledger is locked", @@ -682,6 +685,7 @@ "no_id_needed": "No ID needed!", "no_id_required": "No ID required. Top up and spend anywhere", "no_providers_available": "No providers available", + "no_providers_selected": "No Providers Selected", "no_relay_on_domain": "There isn't a relay for user's domain or the relay is unavailable. Please choose a relay to use.", "no_relays": "No relays", "no_relays_message": "We found a Nostr NIP-05 record for this user, but it does not contain any relays. Please instruct the recipient to add relays to their Nostr record.", @@ -871,6 +875,7 @@ "rescan": "Rescan", "resend_code": "Please resend it", "reset": "Reset", + "reset_filters": "Reset Filters", "reset_password": "Reset Password", "restore": "Restore", "restore_active_seed": "Active seed", @@ -1285,6 +1290,7 @@ "tx_wrong_balance_exception": "You do not have enough ${currency} to send this amount.", "tx_wrong_balance_with_amount_exception": "You do not have enough ${currency} to send the total amount of ${amount}", "tx_zero_fee_exception": "Cannot send transaction with 0 fee. Try increasing the rate or checking your connection for latest estimates.", + "type": "Type", "unavailable_balance": "Unavailable balance", "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your Coin Control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.", "unconfirmed": "Unconfirmed Balance", From f7d80c62a24e96cfe3d4aba364f259f3d03959a8 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 10:36:57 +0200 Subject: [PATCH 03/21] fix send/receive --- .../dashboard/dashboard_view_model.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 02c373f0a..bb34a1345 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -101,14 +101,14 @@ abstract class DashboardViewModelBase with Store { // value: () => transactionFilterStore.displayAll, // caption: S.current.all_transactions, // onChanged: transactionFilterStore.toggleAll), - FilterItem( - value: () => transactionFilterStore.displayIncoming, - caption: S.current.send, - onChanged: transactionFilterStore.toggleIncoming), FilterItem( value: () => transactionFilterStore.displayOutgoing, - caption: S.current.receive, + caption: S.current.send, onChanged: transactionFilterStore.toggleOutgoing), + FilterItem( + value: () => transactionFilterStore.displayIncoming, + caption: S.current.receive, + onChanged: transactionFilterStore.toggleIncoming), if (appStore.wallet!.type == WalletType.bitcoin) FilterItem( value: () => transactionFilterStore.displaySilentPayments, @@ -118,7 +118,7 @@ abstract class DashboardViewModelBase with Store { SwapFilterItem( enabledProviders: () => tradeFilterStore.enabledProviders, allEnabled: () => tradeFilterStore.displayAllTrades, - value: () => tradeFilterStore.displayAllTrades, + value: () => tradeFilterStore.enabledProviders>0, onChanged: () => tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)), FilterItem( @@ -406,6 +406,11 @@ abstract class DashboardViewModelBase with Store { item.onChanged(); } } + for (final item in exchangeFilterItems) { + if (item.value() != value) { + item.onChanged(); + } + } } @action From 681009679512345bab11fff69bae475ab2d242f8 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 10:37:08 +0200 Subject: [PATCH 04/21] fix today threshold --- lib/view_model/dashboard/formatted_item_list.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/view_model/dashboard/formatted_item_list.dart b/lib/view_model/dashboard/formatted_item_list.dart index 2348222c5..45dfb663d 100644 --- a/lib/view_model/dashboard/formatted_item_list.dart +++ b/lib/view_model/dashboard/formatted_item_list.dart @@ -9,7 +9,7 @@ List formattedItemsList(List items) { items.sort((a, b) => b.date.compareTo(a.date)); final now = DateTime.now(); - final last24hThreshold = now.subtract(const Duration(hours: 24)); + final todayTreshold = DateTime(now.year, now.month, now.day); final last7daysThreshold = now.subtract(const Duration(days: 7)); final last30daysThreshold = now.subtract(const Duration(days: 30)); @@ -19,7 +19,7 @@ List formattedItemsList(List items) { for (final transaction in items) { final date = transaction.date; - final bucket = date.isAfter(last24hThreshold) + final bucket = date.isAfter(todayTreshold) ? _DateBucket.recent : date.isAfter(last7daysThreshold) ? _DateBucket.last7Days @@ -33,7 +33,7 @@ List formattedItemsList(List items) { formattedList.add( TodayTransactionItem( date, - key: const ValueKey("last_7_days_section_item_key"), + key: const ValueKey("today_section_item_key"), ), ); } From ba0be28da81fe1cf43090d1b375dddb3bac67d67 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 10:37:16 +0200 Subject: [PATCH 05/21] fix swap filter checkbox --- .../widgets/coins_page/assets_history/history_filters_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart b/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart index 3a71a86cb..072f11015 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart @@ -52,7 +52,7 @@ class HistoryFiltersPage extends StatelessWidget { if (item is SwapFilterItem) { final String subtitle; final Color subtitleColor; - if (item.value()) { + if (dashboardViewModel.tradeFilterStore.displayAllTrades) { subtitle = S.of(context).manage_providers; subtitleColor = Theme.of(context).colorScheme.onSurfaceVariant; } else if (dashboardViewModel.tradeFilterStore.enabledProviders == 0) { From 52b014aac2d44c359f5e6a8be649db972faf3905 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 10:37:28 +0200 Subject: [PATCH 06/21] add border around filters button --- .../widgets/coins_page/assets_history/history_modal.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart b/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart index 9b93a9d0c..864c7fec6 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_modal.dart @@ -86,7 +86,11 @@ class HistoryModal extends StatelessWidget { }, child: Material( color: Colors.transparent, - child: SizedBox( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + border: Border.all(color: Theme.of(context).colorScheme.primary, width: 1) + ), height: 56, width: double.infinity, child: Row( From 117f8e922a8506f14e6a236147814a4c24edc2a9 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 10:37:51 +0200 Subject: [PATCH 07/21] fix observer for history section --- .../assets_history/history_section.dart | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) 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 fd2e08145..bb2016b5b 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 @@ -34,12 +34,14 @@ class HistorySection extends StatelessWidget { @override Widget build(BuildContext context) { - final items = short ? dashboardViewModel.itemsShort : dashboardViewModel.items; return SliverPadding( padding: EdgeInsets.only(left: 16.0, right: 16, top: short && roundedTopSection ? 18 : 0), sliver: Observer( - builder: (_) => (items.isEmpty) + builder: (_) { + final items = short ? dashboardViewModel.itemsShort : dashboardViewModel.items; + + return (items.isEmpty) ? SliverPadding( padding: EdgeInsets.only(top: 24), sliver: SliverToBoxAdapter( @@ -203,7 +205,8 @@ class HistorySection extends StatelessWidget { return Text(item.runtimeType.toString()); }), ), - ), + ); + }, )); } @@ -237,15 +240,15 @@ class HistorySection extends StatelessWidget { if (daysAgo < 7) { final weekday = DateFormat.EEEE().format(date); - return '$weekday, $timeStr'; + return "$weekday, $timeStr"; } if (date.year == now.year) { final dayMonth = DateFormat("d MMMM").format(date); - return '$dayMonth, $timeStr'; + return "$dayMonth, $timeStr"; } - final full = DateFormat("d MMMM yyyy").format(date); - return '$full, $timeStr'; + final full = DateFormat("d MMM yyyy").format(date); + return "$full, $timeStr"; } } From eff46638d9ba26b9bf3b4decb6da71bf143332c4 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 26 Jun 2026 13:25:55 +0200 Subject: [PATCH 08/21] locale fix --- .../assets_history_section.dart | 35 ++++++++--- .../assets_history/assets_top_bar.dart | 58 ++++--------------- .../assets_history/history_section.dart | 24 ++++---- 3 files changed, 48 insertions(+), 69 deletions(-) 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 ecc163092..81fad913d 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 @@ -4,6 +4,7 @@ import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_top_ import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_modal.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_top_bar.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; @@ -13,11 +14,20 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'assets_section.dart'; import 'history_section.dart'; +class AssetsHistorySectionActionButton { + final String title; + final String iconPath; + final VoidCallback onPressed; + + const AssetsHistorySectionActionButton(this.title, this.iconPath, this.onPressed); +} + class AssetsHistorySectionTab { final String title; final Widget content; + final AssetsHistorySectionActionButton? actionButton; - AssetsHistorySectionTab(this.title, this.content); + const AssetsHistorySectionTab(this.title, this.content, this.actionButton); } class AssetsHistorySection extends StatefulWidget { @@ -36,24 +46,31 @@ class _AssetsHistorySectionState extends State { void reloadTabs() { final oldTabLength = tabs.length; + final hasAssetsTab = widget.dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled || (widget.dashboardViewModel.hasMweb && widget.dashboardViewModel.mwebEnabled); + final hasNftTab = isNFTACtivatedChain(widget.dashboardViewModel.wallet.type, + chainId: widget.dashboardViewModel.wallet.chainId); tabs = [ - if (widget.dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled || (widget.dashboardViewModel.hasMweb && widget.dashboardViewModel.mwebEnabled)) + if (hasAssetsTab) AssetsHistorySectionTab( S.current.assets, AssetsSection( dashboardViewModel: widget.dashboardViewModel, - )), + ), AssetsHistorySectionActionButton(S.current.tokens, "assets/new-ui/options_slider.svg", (){ + Navigator.of(context).pushNamed( + Routes.homeSettings, + arguments: widget.dashboardViewModel.balanceViewModel, + ); + })), AssetsHistorySectionTab( S.current.history, HistorySection( detailsAsPage: false, - roundedTopSection: tabs.length > 1, + roundedTopSection: hasAssetsTab || hasNftTab, dashboardViewModel: widget.dashboardViewModel, short: true, - )), - if (isNFTACtivatedChain(widget.dashboardViewModel.wallet.type, - chainId: widget.dashboardViewModel.wallet.chainId)) - AssetsHistorySectionTab(S.current.nfts, NFTListingPage(nftViewModel: widget.nftViewModel)) + ), AssetsHistorySectionActionButton(S.current.all_pascal_case, "assets/new-ui/arrow_right.svg", (){openHistoryModal(context);})), + if (hasNftTab) + AssetsHistorySectionTab(S.current.nfts, NFTListingPage(nftViewModel: widget.nftViewModel), null) ]; if (oldTabLength != tabs.length) { setState(() { @@ -81,7 +98,7 @@ class _AssetsHistorySectionState extends State { AssetsTopBar( onTransactionHistoryOpened: () => openHistoryModal(context), dashboardViewModel: widget.dashboardViewModel, - tabs: tabs.map((item) => item.title).toList(), + tabs: tabs, onTabChange: (index) { setState(() { _selectedTab = index; 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 a99e8f137..67fe4c9a3 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 @@ -1,9 +1,5 @@ -import 'package:cake_wallet/core/csv_export_service.dart'; -import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_history_section.dart'; import 'package:cake_wallet/new-ui/widgets/line_tab_switcher.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:flutter/material.dart'; @@ -20,13 +16,11 @@ class AssetsTopBar extends StatelessWidget { final void Function(int) onTabChange; final VoidCallback onTransactionHistoryOpened; final int selectedTab; - final List tabs; + final List tabs; final DashboardViewModel dashboardViewModel; @override Widget build(BuildContext context) { - final settingsButtonText = _getSettingsButtonText(); - final hasTokenSettingsButton = settingsButtonText != null; return SliverToBoxAdapter( child: Padding( @@ -36,7 +30,7 @@ class AssetsTopBar extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LineTabSwitcher( - tabs: tabs, + tabs: tabs.map((item)=>item.title).toList(), onTabChange: onTabChange, selectedTab: selectedTab, ), @@ -57,19 +51,13 @@ class AssetsTopBar extends StatelessWidget { children: [ Opacity( - opacity: hasTokenSettingsButton ? 1 : 0, + opacity: tabs[selectedTab].actionButton != null ? 1 : 0, child: GestureDetector( onTap: () { - if (tabs[selectedTab] == S.of(context).assets) { - Navigator.of(context).pushNamed( - Routes.homeSettings, - arguments: dashboardViewModel.balanceViewModel, - ); - } else if (tabs[selectedTab] == S.of(context).history) { - - onTransactionHistoryOpened(); - } - }, + if(tabs[selectedTab].actionButton != null) { + tabs[selectedTab].actionButton?.onPressed(); + } + }, child: Container( height: 40, decoration: BoxDecoration( @@ -82,13 +70,13 @@ class AssetsTopBar extends StatelessWidget { spacing: 6, children: [ - if ((settingsButtonText ?? "").isNotEmpty) + if ((tabs[selectedTab].actionButton?.title ?? "").isNotEmpty) Text( - settingsButtonText ?? "", + tabs[selectedTab].actionButton?.title ?? "", style: TextStyle(color: Theme.of(context).colorScheme.primary), ), CakeImageWidget( - imageUrl: _getSettingsButtonIconPath(), + imageUrl: tabs[selectedTab].actionButton?.iconPath, colorFilter: ColorFilter.mode( Theme.of(context).colorScheme.primary, BlendMode.srcIn)), ], @@ -106,28 +94,4 @@ class AssetsTopBar extends StatelessWidget { ); } - String? _getSettingsButtonIconPath() { - if (tabs[selectedTab] == S.current.history) { - return "assets/new-ui/arrow_right.svg"; - } - - if (tabs[selectedTab] == S.current.assets && - dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled) { - return "assets/new-ui/options_slider.svg"; - } - - return null; - } - - String? _getSettingsButtonText() { - if (tabs[selectedTab] == S.current.assets && - dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled) { - return S.current.tokens; - } - - if (tabs[selectedTab] == S.current.history) { - return S.current.all_pascal_case; - } - return null; - } } 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 bb2016b5b..56b07cab3 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 @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/anonpay_history_tile.dart'; @@ -9,7 +7,6 @@ import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/history_tra import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/payjoin_history_tile.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/utils/date_formatter.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/date_section_item.dart'; @@ -39,6 +36,7 @@ class HistorySection extends StatelessWidget { padding: EdgeInsets.only(left: 16.0, right: 16, top: short && roundedTopSection ? 18 : 0), sliver: Observer( builder: (_) { + final localeName = Localizations.localeOf(context).toString(); final items = short ? dashboardViewModel.itemsShort : dashboardViewModel.items; return (items.isEmpty) @@ -100,7 +98,7 @@ class HistorySection extends StatelessWidget { }, child: HistoryTile( title: item.formattedTitle + transactionType, - date: _formatTransactionDate(item.date), + date: _formatTransactionDate(item.date, localeName), amount: item.formattedCryptoAmount, amountFiat: item.formattedFiatAmount, hasTokens: item.hasTokens, @@ -128,7 +126,7 @@ class HistorySection extends StatelessWidget { from: tradeFrom, to: tradeTo, provider: trade.provider, - date: _formatTransactionDate(item.trade.createdAt ?? DateTime.now()), + date: _formatTransactionDate(item.trade.createdAt ?? DateTime.now(), localeName), amount: trade.amountFormatted(), receiveAmount: trade.receiveAmountFormatted(), roundedBottom: roundedBottom, @@ -154,7 +152,7 @@ class HistorySection extends StatelessWidget { onTap: () => Navigator.of(context) .pushNamed(Routes.orderDetails, arguments: item.order), child: HistoryOrderTile( - date: _formatTransactionDate(item.order.createdAt), + date: _formatTransactionDate(item.order.createdAt, localeName), amount: item.orderFormattedAmount, amountFiat: "USD 0.00", roundedBottom: roundedBottom, @@ -171,7 +169,7 @@ class HistorySection extends StatelessWidget { arguments: [item.sessionId, item.transaction], ), child: PayjoinHistoryTile( - createdAt: _formatTransactionDate(session.inProgressSince!), + createdAt: _formatTransactionDate(session.inProgressSince!, localeName), amount: dashboardViewModel.appStore.amountParsingProxy .getDisplayCryptoString( session.amount.toInt(), CryptoCurrency.btc), @@ -190,7 +188,7 @@ class HistorySection extends StatelessWidget { .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), child: AnonpayHistoryTile( provider: transactionInfo.provider, - createdAt: _formatTransactionDate(transactionInfo.createdAt), + createdAt: _formatTransactionDate(transactionInfo.createdAt, localeName), amount: transactionInfo.fiatAmount?.toString() ?? (transactionInfo.amountTo?.toString() ?? ''), currency: transactionInfo.fiatAmount != null @@ -220,8 +218,8 @@ class HistorySection extends StatelessWidget { } } - String _formatTransactionDate(DateTime date) { - final time = DateFormat.Hm(); + String _formatTransactionDate(DateTime date, String localeName) { + final time = DateFormat.Hm(localeName); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); @@ -239,16 +237,16 @@ class HistorySection extends StatelessWidget { } if (daysAgo < 7) { - final weekday = DateFormat.EEEE().format(date); + final weekday = DateFormat.EEEE(localeName).format(date); return "$weekday, $timeStr"; } if (date.year == now.year) { - final dayMonth = DateFormat("d MMMM").format(date); + final dayMonth = DateFormat("d MMMM", localeName).format(date); return "$dayMonth, $timeStr"; } - final full = DateFormat("d MMM yyyy").format(date); + final full = DateFormat("d MMM yyyy", localeName).format(date); return "$full, $timeStr"; } } From 04d22594c8dea3a81f9a4d26bc4210437888e882 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 17:24:15 +0200 Subject: [PATCH 09/21] add spanish for last 7 days --- res/values/strings_es.arb | 1 + 1 file changed, 1 insertion(+) diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 3b5560617..91c41c6f2 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -555,6 +555,7 @@ "keys": "Claves", "label": "Etiqueta", "label_address": "Etiquetar dirección", + "last_7_days": "Últimos 7 días", "last_30_days": "Últimos 30 días", "learn_more": "Más información", "ledger_connection_error": "No se pudo conectar a tu Ledger. Inténtalo de nuevo.", From ddd66b08eab342c73e8978d8948a1c21b812df38 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 17:24:38 +0200 Subject: [PATCH 10/21] properly translate month names in history --- .../widgets/coins_page/assets_history/history_section.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 212b7e2cd..2bdd715ac 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 @@ -143,7 +143,7 @@ class HistorySection extends StatelessWidget { } else if (item is DateSectionItem) { return Padding( padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), - child: Text(DateFormat("MMMM yyyy").format(item.date), + child: Text(DateFormat("MMMM yyyy", localeName).format(item.date), style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant))); } else if (item is OrderListItem) { From 63651aa39c82fc6c8f63ef22d89f9096176fcc91 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 17:24:56 +0200 Subject: [PATCH 11/21] fix line tab switcher updates --- lib/new-ui/widgets/line_tab_switcher.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/new-ui/widgets/line_tab_switcher.dart b/lib/new-ui/widgets/line_tab_switcher.dart index b20cd23f7..15927aff6 100644 --- a/lib/new-ui/widgets/line_tab_switcher.dart +++ b/lib/new-ui/widgets/line_tab_switcher.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -29,7 +30,7 @@ class _LineTabSwitcherState extends State { void didUpdateWidget(covariant LineTabSwitcher oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.tabs.length != widget.tabs.length) { + if (!listEquals(oldWidget.tabs, widget.tabs)) { textWidgetKeys = List.generate(widget.tabs.length, (_) => GlobalKey()); textWidgetSizes = []; totalWidth = 0; From 46cda778d8a1234eda586f7b3e72a23520e5ef5a Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 17:25:13 +0200 Subject: [PATCH 12/21] fix date format in tx details --- lib/view_model/transaction_details_view_model.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index 7199ed1a9..18c3c9a2e 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -30,6 +30,7 @@ import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; +import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -77,8 +78,7 @@ class TxDetailRowDefinition { TxDetailRowDefinition( keyString: "standard_list_item_transaction_details_date_key", title: S.current.transaction_details_date, - valueGetter: (vm) => DateFormatter.withCurrentLocal().format(vm.transactionInfo.date)), - + valueGetter: (vm) => DateFormat("d MMMM yyyy, HH:mm", vm._appStore.settingsStore.languageCode).format(vm.transactionInfo.date)), TxDetailRowDefinition( keyString: "standard_list_item_transaction_details_height_key", title: S.current.transaction_details_height, From bfeae1eca830cf88a92fef4e0a8195ea39b11505 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Wed, 1 Jul 2026 17:25:22 +0200 Subject: [PATCH 13/21] fix evm decimals in history --- lib/view_model/dashboard/transaction_list_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index 0a38ff2d2..d29fd5296 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -51,7 +51,7 @@ class TransactionListItem extends ActionListItem with Keyable { .withLocalSeperator(_appStore.settingsStore.languageCode); } - return transaction.amount.toStringWithSymbol(); + return transaction.amount.toStringWithSymbol(fractionalDigits: 8); } String get formattedTitle { From 1af6ab00eb4fd4c375b6888e75b9180ab4038eb2 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 14:46:23 +0200 Subject: [PATCH 14/21] layout fix --- .../coins_page/assets_history/assets_history_section.dart | 1 + .../widgets/coins_page/assets_history/history_top_bar.dart | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) 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 81fad913d..c3f699d52 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 @@ -109,6 +109,7 @@ class _AssetsHistorySectionState extends State { if (tabs.length == 1) HistoryTopBar( onTap: () => openHistoryModal(context), + roundedBottom: widget.dashboardViewModel.itemsShort.isEmpty, ), tabs[_selectedTab].content ], diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart index c8749b731..cbb80111b 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart @@ -3,9 +3,10 @@ import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:flutter/material.dart'; class HistoryTopBar extends StatelessWidget { - const HistoryTopBar({super.key, required this.onTap}); + const HistoryTopBar({super.key, required this.onTap, required this.roundedBottom}); final VoidCallback onTap; + final bool roundedBottom; @override Widget build(BuildContext context) { @@ -19,10 +20,12 @@ class HistoryTopBar extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.vertical( top: Radius.circular(18), + bottom: roundedBottom ? Radius.zero : Radius.circular(18) ), color: Theme.of(context).colorScheme.surfaceContainer), child: Padding( padding: EdgeInsets.symmetric( + vertical: 8, horizontal: 12, ), child: Column( From 7b17459ed00baded5be3c42bcbe58f76c8f4772d Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 14:55:10 +0200 Subject: [PATCH 15/21] layout fix --- .../widgets/coins_page/assets_history/history_top_bar.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart index cbb80111b..7a3cf0651 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart @@ -20,12 +20,12 @@ class HistoryTopBar extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.vertical( top: Radius.circular(18), - bottom: roundedBottom ? Radius.zero : Radius.circular(18) + bottom: roundedBottom ? Radius.circular(18) : Radius.zero ), color: Theme.of(context).colorScheme.surfaceContainer), child: Padding( padding: EdgeInsets.symmetric( - vertical: 8, + vertical: 4, horizontal: 12, ), child: Column( @@ -43,6 +43,7 @@ class HistoryTopBar extends StatelessWidget { ) ], ), + if(!roundedBottom) Container( height: 1, width: double.infinity, From 14dcea5e1a04c643ec7d8fa617e8c95ddbffee03 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 14:55:23 +0200 Subject: [PATCH 16/21] fix silent payments appearing in non-btc wallets --- .../dashboard/dashboard_view_model.dart | 207 +++++++++--------- 1 file changed, 109 insertions(+), 98 deletions(-) diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index bb34a1345..62d916fbc 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -96,104 +96,8 @@ abstract class DashboardViewModelBase with Store { isShowFirstYatIntroduction = false, isShowSecondYatIntroduction = false, isShowThirdYatIntroduction = false, - filterItems = [ - // FilterItem( - // value: () => transactionFilterStore.displayAll, - // caption: S.current.all_transactions, - // onChanged: transactionFilterStore.toggleAll), - FilterItem( - value: () => transactionFilterStore.displayOutgoing, - caption: S.current.send, - onChanged: transactionFilterStore.toggleOutgoing), - FilterItem( - value: () => transactionFilterStore.displayIncoming, - caption: S.current.receive, - onChanged: transactionFilterStore.toggleIncoming), - if (appStore.wallet!.type == WalletType.bitcoin) - FilterItem( - value: () => transactionFilterStore.displaySilentPayments, - caption: S.current.silent_payments, - onChanged: transactionFilterStore.toggleSilentPayments, - ), - SwapFilterItem( - enabledProviders: () => tradeFilterStore.enabledProviders, - allEnabled: () => tradeFilterStore.displayAllTrades, - value: () => tradeFilterStore.enabledProviders>0, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)), - FilterItem( - value: () => orderFilterStore.displayCakePay, - caption: 'Cake Pay', - onChanged: () => - orderFilterStore.toggleDisplayOrder(OrderProviderDescription.cakePay)), - ], - exchangeFilterItems = [ - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.changeNow, - value: () => tradeFilterStore.displayChangeNow, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.sideShift, - value: () => tradeFilterStore.displaySideShift, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.simpleSwap, - value: () => tradeFilterStore.displaySimpleSwap, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.trocador, - value: () => tradeFilterStore.displayTrocador, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.exolix, - value: () => tradeFilterStore.displayExolix, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.chainflip, - value: () => tradeFilterStore.displayChainflip, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.chainflip)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.thorChain, - value: () => tradeFilterStore.displayThorChain, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.letsExchange, - value: () => tradeFilterStore.displayLetsExchange, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.stealthEx, - value: () => tradeFilterStore.displayStealthEx, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.xoSwap, - value: () => tradeFilterStore.displayXOSwap, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.xoSwap)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.swapTrade, - value: () => tradeFilterStore.displaySwapTrade, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapTrade)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.swapsXyz, - value: () => tradeFilterStore.displaySwapXyz, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapsXyz)), - SwapProviderFilterItem( - providerDescription: ExchangeProviderDescription.nearIntents, - value: () => tradeFilterStore.displayNearIntents, - onChanged: () => - tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.nearIntents)), - ], + filterItems = [], + exchangeFilterItems = [], subname = '', name = appStore.wallet!.name, type = appStore.wallet!.type, @@ -214,6 +118,8 @@ abstract class DashboardViewModelBase with Store { unawaited(_loadConstraints()); final _wallet = wallet; + loadFilterItems(); + if (_wallet.type == WalletType.monero) { subname = monero!.getCurrentAccount(_wallet).label; @@ -346,6 +252,110 @@ abstract class DashboardViewModelBase with Store { tradeMonitor.monitorActiveTrades(wallet.id); } + + void loadFilterItems() { + filterItems = [ + // FilterItem( + // value: () => transactionFilterStore.displayAll, + // caption: S.current.all_transactions, + // onChanged: transactionFilterStore.toggleAll), + FilterItem( + value: () => transactionFilterStore.displayOutgoing, + caption: S.current.send, + onChanged: transactionFilterStore.toggleOutgoing), + FilterItem( + value: () => transactionFilterStore.displayIncoming, + caption: S.current.receive, + onChanged: transactionFilterStore.toggleIncoming), + if (appStore.wallet!.type == WalletType.bitcoin) + FilterItem( + value: () => transactionFilterStore.displaySilentPayments, + caption: S.current.silent_payments, + onChanged: transactionFilterStore.toggleSilentPayments, + ), + SwapFilterItem( + enabledProviders: () => tradeFilterStore.enabledProviders, + allEnabled: () => tradeFilterStore.displayAllTrades, + value: () => tradeFilterStore.enabledProviders>0, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)), + FilterItem( + value: () => orderFilterStore.displayCakePay, + caption: 'Cake Pay', + onChanged: () => + orderFilterStore.toggleDisplayOrder(OrderProviderDescription.cakePay)), + ]; + exchangeFilterItems = [ + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.changeNow, + value: () => tradeFilterStore.displayChangeNow, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.sideShift, + value: () => tradeFilterStore.displaySideShift, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.simpleSwap, + value: () => tradeFilterStore.displaySimpleSwap, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.trocador, + value: () => tradeFilterStore.displayTrocador, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.exolix, + value: () => tradeFilterStore.displayExolix, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.chainflip, + value: () => tradeFilterStore.displayChainflip, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.chainflip)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.thorChain, + value: () => tradeFilterStore.displayThorChain, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.letsExchange, + value: () => tradeFilterStore.displayLetsExchange, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.stealthEx, + value: () => tradeFilterStore.displayStealthEx, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.xoSwap, + value: () => tradeFilterStore.displayXOSwap, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.xoSwap)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.swapTrade, + value: () => tradeFilterStore.displaySwapTrade, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapTrade)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.swapsXyz, + value: () => tradeFilterStore.displaySwapXyz, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.swapsXyz)), + SwapProviderFilterItem( + providerDescription: ExchangeProviderDescription.nearIntents, + value: () => tradeFilterStore.displayNearIntents, + onChanged: () => + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.nearIntents)), + ]; + } + + + bool _isTransactionDisposerCallbackRunning = false; @action @@ -1254,6 +1264,7 @@ abstract class DashboardViewModelBase with Store { this.wallet = wallet; type = wallet.type; name = wallet.name; + loadFilterItems(); if (wallet.type == WalletType.monero) { subname = monero!.getCurrentAccount(wallet).label; From 764f3d9ea568ab83fe73d20e4c39c3e148cd873f Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 15:44:25 +0200 Subject: [PATCH 17/21] fix history buton padding w no txs --- .../widgets/coins_page/assets_history/history_top_bar.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart index 7a3cf0651..a46bb1ced 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_top_bar.dart @@ -49,6 +49,7 @@ class HistoryTopBar extends StatelessWidget { width: double.infinity, color: Theme.of(context).colorScheme.outlineVariant.withAlpha(175), ) + else Container(height: 2) ], ), ), From 474312464b469fbee279ad4aae196dca030c3f52 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 16:28:12 +0200 Subject: [PATCH 18/21] wrap HistoryTopBar in Observer --- .../assets_history/assets_history_section.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 c3f699d52..8d5dfce09 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 @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'assets_section.dart'; @@ -107,9 +108,11 @@ class _AssetsHistorySectionState extends State { selectedTab: _selectedTab, ), if (tabs.length == 1) - HistoryTopBar( - onTap: () => openHistoryModal(context), - roundedBottom: widget.dashboardViewModel.itemsShort.isEmpty, + Observer( + builder:(_)=> HistoryTopBar( + onTap: () => openHistoryModal(context), + roundedBottom: widget.dashboardViewModel.itemsShort.isEmpty, + ), ), tabs[_selectedTab].content ], From 92f1314d940feb0afc5cbe7e681ac2599cd238e3 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 3 Jul 2026 17:55:06 +0200 Subject: [PATCH 19/21] reset filters after closing history modal --- .../coins_page/assets_history/assets_history_section.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 8d5dfce09..79eb395f5 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 @@ -119,8 +119,8 @@ class _AssetsHistorySectionState extends State { ); } - void openHistoryModal(BuildContext context) { - CupertinoScaffold.showCupertinoModalBottomSheet( + Future openHistoryModal(BuildContext context) async { + await CupertinoScaffold.showCupertinoModalBottomSheet( context: context, builder: (context) => ModalNavigator( rootPage: Material( @@ -128,5 +128,6 @@ class _AssetsHistorySectionState extends State { child: HistoryModal(dashboardViewModel: widget.dashboardViewModel)), parentContext: context, )); + widget.dashboardViewModel.changeAllFilterItems(true); } } From 839530063172bede108c61bbaaccd3539febc2ae Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Sun, 5 Jul 2026 17:10:26 +0300 Subject: [PATCH 20/21] remove old providers [skip ci] --- lib/store/dashboard/trade_filter_store.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index abc4c337a..2fee9cc70 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -71,10 +71,8 @@ abstract class TradeFilterStoreBase with Store { @computed int get enabledProviders => [ - displayXMRTO, displayChangeNow, displaySideShift, - displayMorphToken, displaySimpleSwap, displayTrocador, displayExolix, From 90cbf6777a3da8090c8e368d050279ff7cedf827 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Mon, 13 Jul 2026 15:54:49 +0200 Subject: [PATCH 21/21] add padding --- .../assets_history/history_filters_page.dart | 6 +- .../assets_history/history_section.dart | 275 +++++++++--------- .../history_swap_providers_page.dart | 4 +- lib/store/dashboard/trade_filter_store.dart | 2 +- .../dashboard/dashboard_view_model.dart | 4 +- 5 files changed, 147 insertions(+), 144 deletions(-) diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart b/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart index 072f11015..a42edeb68 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_filters_page.dart @@ -55,7 +55,7 @@ class HistoryFiltersPage extends StatelessWidget { if (dashboardViewModel.tradeFilterStore.displayAllTrades) { subtitle = S.of(context).manage_providers; subtitleColor = Theme.of(context).colorScheme.onSurfaceVariant; - } else if (dashboardViewModel.tradeFilterStore.enabledProviders == 0) { + } else if (dashboardViewModel.tradeFilterStore.enabledProvidersCount == 0) { subtitle = S.of(context).no_providers_selected; subtitleColor = Color(0xFFFFB84E); } else { @@ -74,10 +74,10 @@ class HistoryFiltersPage extends StatelessWidget { value: item.value(), onChanged: (val) { if ((val && - dashboardViewModel.tradeFilterStore.enabledProviders == + dashboardViewModel.tradeFilterStore.enabledProvidersCount == 0) || (!val && - dashboardViewModel.tradeFilterStore.enabledProviders > + dashboardViewModel.tradeFilterStore.enabledProvidersCount > 0)) { dashboardViewModel.tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.all); 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 2bdd715ac..f6bab4488 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 @@ -56,153 +56,156 @@ class HistorySection extends StatelessWidget { ), ), ) - : SliverList( - delegate: SliverChildBuilderDelegate( - childCount: items.length, - (context, index) => Observer(builder: (_) { - final prevItem = index == 0 ? null : items[index - 1]; - final topPadding = index == 0 ? 0.0 : 18.0; - final item = items[index]; - final nextItem = index == items.length - 1 - ? null - : items[index + 1]; + : SliverPadding( + padding: EdgeInsets.only(bottom: short ? 0 : 144), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + childCount: items.length, + (context, index) => Observer(builder: (_) { + final prevItem = index == 0 ? null : items[index - 1]; + final topPadding = index == 0 ? 0.0 : 18.0; + final item = items[index]; + final nextItem = index == items.length - 1 + ? null + : items[index + 1]; - final roundedBottom = (nextItem == null || nextItem is DateSectionItem); - final roundedTop = roundedTopSection && (prevItem == null || prevItem is DateSectionItem); + final roundedBottom = (nextItem == null || nextItem is DateSectionItem); + final roundedTop = roundedTopSection && (prevItem == null || prevItem is DateSectionItem); - if (item is TransactionListItem) { - final transaction = item.transaction; - final transactionType = dashboardViewModel.getTransactionType(transaction); + if (item is TransactionListItem) { + final transaction = item.transaction; + final transactionType = dashboardViewModel.getTransactionType(transaction); - if (item.hasTokens && item.assetOfTransaction == null) { - return Container(); - } + if (item.hasTokens && item.assetOfTransaction == null) { + return Container(); + } - CryptoCurrency? asset; - if (transaction.additionalInfo["isLightning"] == true) - asset = CryptoCurrency.btcln; - else - asset = item.assetOfTransaction; + CryptoCurrency? asset; + if (transaction.additionalInfo["isLightning"] == true) + asset = CryptoCurrency.btcln; + else + asset = item.assetOfTransaction; - return GestureDetector( - onTap: () { - final page = getIt.get(param1: transaction); - if (detailsAsPage) { - Navigator.of(context).push( - CupertinoPageRoute(builder: (context) => Material(child: page))); - } else { - showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: (context) => - FractionallySizedBox(heightFactor: 0.9, child: page)); - } - }, - child: HistoryTile( - title: item.formattedTitle + transactionType, - date: _formatTransactionDate(item.date, localeName), - amount: item.formattedCryptoAmount, - amountFiat: item.formattedFiatAmount, - hasTokens: item.hasTokens, - chainIconPath: _getChainIconPath(), - roundedBottom: roundedBottom, - roundedTop: roundedTop, - bottomSeparator: !roundedBottom, - direction: item.transaction.direction, - pending: item.transaction.isPending, - asset: asset, - ), - ); - } else if (item is TradeListItem) { - final trade = item.trade; - final tradeFrom = trade.from; - final tradeTo = trade.to; - - return GestureDetector( - onTap: () => Navigator.of(context) - .pushNamed(Routes.tradeDetails, arguments: trade), - child: HistoryTradeTile( - from: tradeFrom, - to: tradeTo, - provider: trade.provider, - date: _formatTransactionDate(item.trade.createdAt ?? DateTime.now(), localeName), - amount: dashboardViewModel.balanceDisplayMode == BalanceDisplayMode.hiddenBalance ? "---" : trade.amountFormatted(), - receiveAmount: dashboardViewModel.balanceDisplayMode == BalanceDisplayMode.hiddenBalance ? "---" : trade.receiveAmountFormatted(), - roundedBottom: roundedBottom, - roundedTop: roundedTop, - bottomSeparator: !roundedBottom, - swapState: trade.state, - ), - ); - } else if (item is SpecificDateSectionItem) { - return Padding( - padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), - child: Text(item.text, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant))); - } else if (item is DateSectionItem) { - return Padding( - padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), - child: Text(DateFormat("MMMM yyyy", localeName).format(item.date), - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant))); - } else if (item is OrderListItem) { - return GestureDetector( - onTap: () => Navigator.of(context) - .pushNamed(Routes.orderDetails, arguments: item.order), - child: HistoryOrderTile( - date: _formatTransactionDate(item.order.createdAt, localeName), - amount: item.orderFormattedAmount, - amountFiat: "", - roundedBottom: roundedBottom, - roundedTop: roundedTop, - bottomSeparator: !roundedBottom, - ), - ); - } else if (item is PayjoinTransactionListItem) { - final session = item.session; - - return GestureDetector( - onTap: () => Navigator.of(context).pushNamed( - Routes.payjoinDetails, - arguments: [item.sessionId, item.transaction], - ), - child: PayjoinHistoryTile( - createdAt: _formatTransactionDate(session.inProgressSince!, localeName), - amount: dashboardViewModel.appStore.amountParsingProxy - .asDisplayString(Money( - session.amount, CryptoCurrency.btc)), - currency: item.transaction?.from ?? "BTC", - state: item.status, - isSending: session.isSenderSession, - roundedTop: roundedTop, + return GestureDetector( + onTap: () { + final page = getIt.get(param1: transaction); + if (detailsAsPage) { + Navigator.of(context).push( + CupertinoPageRoute(builder: (context) => Material(child: page))); + } else { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => + FractionallySizedBox(heightFactor: 0.9, child: page)); + } + }, + child: HistoryTile( + title: item.formattedTitle + transactionType, + date: _formatTransactionDate(item.date, localeName), + amount: item.formattedCryptoAmount, + amountFiat: item.formattedFiatAmount, + hasTokens: item.hasTokens, + chainIconPath: _getChainIconPath(), roundedBottom: roundedBottom, - bottomSeparator: !roundedBottom), - ); - } else if (item is AnonpayTransactionListItem) { - final transactionInfo = item.transaction; + roundedTop: roundedTop, + bottomSeparator: !roundedBottom, + direction: item.transaction.direction, + pending: item.transaction.isPending, + asset: asset, + ), + ); + } else if (item is TradeListItem) { + final trade = item.trade; + final tradeFrom = trade.from; + final tradeTo = trade.to; - return GestureDetector( + return GestureDetector( onTap: () => Navigator.of(context) - .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), - child: AnonpayHistoryTile( - provider: transactionInfo.provider, - createdAt: _formatTransactionDate(transactionInfo.createdAt, localeName), - amount: transactionInfo.fiatAmount?.toString() ?? - (transactionInfo.amountTo?.toString() ?? ''), - currency: transactionInfo.fiatAmount != null - ? transactionInfo.fiatEquiv ?? '' - : CryptoCurrency.fromFullName(transactionInfo.coinTo) - .name - .toUpperCase(), + .pushNamed(Routes.tradeDetails, arguments: trade), + child: HistoryTradeTile( + from: tradeFrom, + to: tradeTo, + provider: trade.provider, + date: _formatTransactionDate(item.trade.createdAt ?? DateTime.now(), localeName), + amount: dashboardViewModel.balanceDisplayMode == BalanceDisplayMode.hiddenBalance ? "---" : trade.amountFormatted(), + receiveAmount: dashboardViewModel.balanceDisplayMode == BalanceDisplayMode.hiddenBalance ? "---" : trade.receiveAmountFormatted(), + roundedBottom: roundedBottom, + roundedTop: roundedTop, + bottomSeparator: !roundedBottom, + swapState: trade.state, + ), + ); + } else if (item is SpecificDateSectionItem) { + return Padding( + padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), + child: Text(item.text, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant))); + } else if (item is DateSectionItem) { + return Padding( + padding: EdgeInsets.only(left: 8.0, bottom: 8.0, top: topPadding), + child: Text(DateFormat("MMMM yyyy", localeName).format(item.date), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant))); + } else if (item is OrderListItem) { + return GestureDetector( + onTap: () => Navigator.of(context) + .pushNamed(Routes.orderDetails, arguments: item.order), + child: HistoryOrderTile( + date: _formatTransactionDate(item.order.createdAt, localeName), + amount: item.orderFormattedAmount, + amountFiat: "", + roundedBottom: roundedBottom, + roundedTop: roundedTop, + bottomSeparator: !roundedBottom, + ), + ); + } else if (item is PayjoinTransactionListItem) { + final session = item.session; + + return GestureDetector( + onTap: () => Navigator.of(context).pushNamed( + Routes.payjoinDetails, + arguments: [item.sessionId, item.transaction], + ), + child: PayjoinHistoryTile( + createdAt: _formatTransactionDate(session.inProgressSince!, localeName), + amount: dashboardViewModel.appStore.amountParsingProxy + .asDisplayString(Money( + session.amount, CryptoCurrency.btc)), + currency: item.transaction?.from ?? "BTC", + state: item.status, + isSending: session.isSenderSession, roundedTop: roundedTop, roundedBottom: roundedBottom, - bottomSeparator: !roundedBottom)); - } else - return Text(item.runtimeType.toString()); - }), + bottomSeparator: !roundedBottom), + ); + } else if (item is AnonpayTransactionListItem) { + final transactionInfo = item.transaction; + + return GestureDetector( + onTap: () => Navigator.of(context) + .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), + child: AnonpayHistoryTile( + provider: transactionInfo.provider, + createdAt: _formatTransactionDate(transactionInfo.createdAt, localeName), + amount: transactionInfo.fiatAmount?.toString() ?? + (transactionInfo.amountTo?.toString() ?? ''), + currency: transactionInfo.fiatAmount != null + ? transactionInfo.fiatEquiv ?? '' + : CryptoCurrency.fromFullName(transactionInfo.coinTo) + .name + .toUpperCase(), + roundedTop: roundedTop, + roundedBottom: roundedBottom, + bottomSeparator: !roundedBottom)); + } else + return Text(item.runtimeType.toString()); + }), + ), ), - ); + ); }, )); } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart b/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart index 1010e4c4a..46de3fb36 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_swap_providers_page.dart @@ -42,9 +42,9 @@ class HistorySwapProvidersPage extends StatelessWidget { title: S.of(context).swap_providers, onSelected: (val) { if ((val && - dashboardViewModel.tradeFilterStore.enabledProviders == 0) || + dashboardViewModel.tradeFilterStore.enabledProvidersCount == 0) || (!val && - dashboardViewModel.tradeFilterStore.enabledProviders > 0)) { + dashboardViewModel.tradeFilterStore.enabledProvidersCount > 0)) { dashboardViewModel.tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.all); } diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index 2fee9cc70..57e3149ee 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -70,7 +70,7 @@ abstract class TradeFilterStoreBase with Store { bool displayNearIntents; @computed - int get enabledProviders => [ + int get enabledProvidersCount => [ displayChangeNow, displaySideShift, displaySimpleSwap, diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 8bdb6d4e9..77c59b762 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -274,9 +274,9 @@ abstract class DashboardViewModelBase with Store { onChanged: transactionFilterStore.toggleSilentPayments, ), SwapFilterItem( - enabledProviders: () => tradeFilterStore.enabledProviders, + enabledProviders: () => tradeFilterStore.enabledProvidersCount, allEnabled: () => tradeFilterStore.displayAllTrades, - value: () => tradeFilterStore.enabledProviders>0, + value: () => tradeFilterStore.enabledProvidersCount>0, onChanged: () => tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)), FilterItem(