From 310bd9e3687de2648d086a2ba161738fede5db37 Mon Sep 17 00:00:00 2001 From: "Charl (Nitride)" <77973576+CharlVS@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:27:58 +0200 Subject: [PATCH 1/2] fix: handle missing swap info --- lib/blocs/trading_entities_bloc.dart | 5 +++-- lib/model/swap.dart | 12 +++++++----- lib/views/dex/dex_helpers.dart | 5 +++-- .../dex/entities_list/history/history_item.dart | 4 +++- .../history/swap_history_sort_mixin.dart | 4 ++-- .../entities_list/in_progress/in_progress_item.dart | 3 ++- .../entities_list/in_progress/in_progress_list.dart | 4 ++-- lib/views/dex/entity_details/swap/swap_details.dart | 9 +++++---- .../entity_details/swap/swap_details_step_list.dart | 3 ++- 9 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/blocs/trading_entities_bloc.dart b/lib/blocs/trading_entities_bloc.dart index 7dd9b4f891..4e4b2d2651 100644 --- a/lib/blocs/trading_entities_bloc.dart +++ b/lib/blocs/trading_entities_bloc.dart @@ -55,7 +55,8 @@ class TradingEntitiesBloc implements BlocBase { List get swaps => _swaps; set swaps(List swapList) { swapList.sort( - (first, second) => second.myInfo.startedAt - first.myInfo.startedAt); + (first, second) => + (second.myInfo?.startedAt ?? 0) - (first.myInfo?.startedAt ?? 0)); _swaps = swapList; _inSwaps.add(_swaps); } @@ -126,7 +127,7 @@ class TradingEntitiesBloc implements BlocBase { final double swapFill = swaps.fold( 0, (previousValue, swap) => - previousValue + swap.myInfo.myAmount.toDouble()); + previousValue + (swap.myInfo?.myAmount ?? 0)); return swapFill / order.baseAmount.toDouble(); } diff --git a/lib/model/swap.dart b/lib/model/swap.dart index d600a5c0f5..4b40e2e211 100644 --- a/lib/model/swap.dart +++ b/lib/model/swap.dart @@ -20,7 +20,7 @@ class Swap extends Equatable { required this.mmVersion, required this.successEvents, required this.errorEvents, - required this.myInfo, + this.myInfo, required this.recoverable, }); @@ -46,7 +46,9 @@ class Swap extends Equatable { mmVersion: json['mm_version'] ?? '', successEvents: List.castFrom(json['success_events']), errorEvents: List.castFrom(json['error_events']), - myInfo: SwapMyInfo.fromJson(json['my_info']), + myInfo: json['my_info'] != null + ? SwapMyInfo.fromJson(Map.from(json['my_info'])) + : null, recoverable: json['recoverable'] ?? false, ); } @@ -63,7 +65,7 @@ class Swap extends Equatable { final String mmVersion; final List successEvents; final List errorEvents; - final SwapMyInfo myInfo; + final SwapMyInfo? myInfo; final bool recoverable; Map toJson() { @@ -83,7 +85,7 @@ class Swap extends Equatable { data['mm_version'] = mmVersion; data['success_events'] = successEvents; data['error_events'] = errorEvents; - data['my_info'] = myInfo.toJson(); + data['my_info'] = myInfo?.toJson(); data['recoverable'] = recoverable; return data; @@ -162,7 +164,7 @@ class Swap extends Equatable { } @override - List get props => [ + List get props => [ type, uuid, myOrderUuid, diff --git a/lib/views/dex/dex_helpers.dart b/lib/views/dex/dex_helpers.dart index f11ad3548e..8f3304678d 100644 --- a/lib/views/dex/dex_helpers.dart +++ b/lib/views/dex/dex_helpers.dart @@ -64,11 +64,12 @@ List applyFiltersForSwap( if (sellCoin != null && swap.sellCoin != sellCoin) return false; if (buyCoin != null && swap.buyCoin != buyCoin) return false; - if (startDate != null && swap.myInfo.startedAt < startDate / 1000) { + if (startDate != null && + (swap.myInfo?.startedAt ?? 0) < startDate / 1000) { return false; } if (endDate != null && - swap.myInfo.startedAt > (endDate + millisecondsIn24H) / 1000) { + (swap.myInfo?.startedAt ?? 0) > (endDate + millisecondsIn24H) / 1000) { return false; } if (statuses != null && statuses.isNotEmpty) { diff --git a/lib/views/dex/entities_list/history/history_item.dart b/lib/views/dex/entities_list/history/history_item.dart index 037db7b628..f28517a90a 100644 --- a/lib/views/dex/entities_list/history/history_item.dart +++ b/lib/views/dex/entities_list/history/history_item.dart @@ -36,7 +36,9 @@ class _HistoryItemState extends State { final Rational sellAmount = widget.swap.sellAmount; final String buyCoin = widget.swap.buyCoin; final Rational buyAmount = widget.swap.buyAmount; - final String date = getFormattedDate(widget.swap.myInfo.startedAt); + final String date = widget.swap.myInfo != null + ? getFormattedDate(widget.swap.myInfo!.startedAt) + : '-'; final bool isSuccessful = !widget.swap.isFailed; final bool isTaker = widget.swap.isTaker; final bool isRecoverable = widget.swap.recoverable; diff --git a/lib/views/dex/entities_list/history/swap_history_sort_mixin.dart b/lib/views/dex/entities_list/history/swap_history_sort_mixin.dart index 00c1f52024..25ef855fed 100644 --- a/lib/views/dex/entities_list/history/swap_history_sort_mixin.dart +++ b/lib/views/dex/entities_list/history/swap_history_sort_mixin.dart @@ -116,8 +116,8 @@ mixin SwapHistorySortingMixin { }) { swaps.sort( (first, second) => sortByDouble( - first.myInfo.startedAt.toDouble(), - second.myInfo.startedAt.toDouble(), + first.myInfo?.startedAt.toDouble() ?? 0, + second.myInfo?.startedAt.toDouble() ?? 0, sortDirection, ), ); diff --git a/lib/views/dex/entities_list/in_progress/in_progress_item.dart b/lib/views/dex/entities_list/in_progress/in_progress_item.dart index 6675ad9064..a42a4aa728 100644 --- a/lib/views/dex/entities_list/in_progress/in_progress_item.dart +++ b/lib/views/dex/entities_list/in_progress/in_progress_item.dart @@ -27,7 +27,8 @@ class InProgressItem extends StatelessWidget { final Rational sellAmount = swap.sellAmount; final String buyCoin = swap.buyCoin; final Rational buyAmount = swap.buyAmount; - final String date = getFormattedDate(swap.myInfo.startedAt); + final String date = + swap.myInfo != null ? getFormattedDate(swap.myInfo!.startedAt) : '-'; final bool isTaker = swap.isTaker; final tradingEntitiesBloc = RepositoryProvider.of(context); diff --git a/lib/views/dex/entities_list/in_progress/in_progress_list.dart b/lib/views/dex/entities_list/in_progress/in_progress_list.dart index b313f36329..da953fb009 100644 --- a/lib/views/dex/entities_list/in_progress/in_progress_list.dart +++ b/lib/views/dex/entities_list/in_progress/in_progress_list.dart @@ -172,8 +172,8 @@ class _InProgressListState extends State { List _sortByDate(List swaps) { swaps.sort((first, second) => sortByDouble( - first.myInfo.startedAt.toDouble(), - second.myInfo.startedAt.toDouble(), + first.myInfo?.startedAt.toDouble() ?? 0, + second.myInfo?.startedAt.toDouble() ?? 0, _sortData.sortDirection, )); return swaps; diff --git a/lib/views/dex/entity_details/swap/swap_details.dart b/lib/views/dex/entity_details/swap/swap_details.dart index b0443fa04c..f024b3c154 100644 --- a/lib/views/dex/entity_details/swap/swap_details.dart +++ b/lib/views/dex/entity_details/swap/swap_details.dart @@ -48,10 +48,11 @@ class SwapDetails extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - TradingDetailsTotalTime( - startedTime: swapStatus.myInfo.startedAt * 1000, - finishedTime: _finishedTime, - ), + if (swapStatus.myInfo != null) + TradingDetailsTotalTime( + startedTime: swapStatus.myInfo!.startedAt * 1000, + finishedTime: _finishedTime, + ), const SizedBox(height: 24), Flexible( child: Padding( diff --git a/lib/views/dex/entity_details/swap/swap_details_step_list.dart b/lib/views/dex/entity_details/swap/swap_details_step_list.dart index b6c0965c40..4c49a862e3 100644 --- a/lib/views/dex/entity_details/swap/swap_details_step_list.dart +++ b/lib/views/dex/entity_details/swap/swap_details_step_list.dart @@ -82,7 +82,8 @@ class SwapDetailsStepList extends StatelessWidget { final int currentEventIndex = swapStatus.events .indexWhere((e) => e.event.type == currentEvent.event.type); if (currentEventIndex == 0) { - return currentEvent.timestamp - swapStatus.myInfo.startedAt * 1000; + return currentEvent.timestamp - + (swapStatus.myInfo?.startedAt ?? 0) * 1000; } final SwapEventItem previousEvent = swapStatus.events[currentEventIndex - 1]; From 8c0e8326e33fcf2b3ac1111e542ea24cd4347025 Mon Sep 17 00:00:00 2001 From: "Charl (Nitride)" <77973576+CharlVS@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:38:38 +0200 Subject: [PATCH 2/2] test: add my recent swaps deserialization (#2823) --- test_units/main.dart | 5 + .../swaps/my_recent_swaps_response_test.dart | 109 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 test_units/tests/swaps/my_recent_swaps_response_test.dart diff --git a/test_units/main.dart b/test_units/main.dart index 7f2f44924d..501e309893 100644 --- a/test_units/main.dart +++ b/test_units/main.dart @@ -33,6 +33,7 @@ import 'tests/utils/convert_double_to_string_test.dart'; import 'tests/utils/convert_fract_rat_test.dart'; import 'tests/utils/double_to_string_test.dart'; import 'tests/utils/get_fiat_amount_tests.dart'; +import 'tests/swaps/my_recent_swaps_response_test.dart'; /// Run in terminal flutter test test_units/main.dart /// More info at documentation "Unit and Widget testing" section @@ -85,6 +86,10 @@ void main() { testEncryptDataTool(); }); + group('MyRecentSwaps:', () { + testMyRecentSwapsResponse(); + }); + group('CexMarketData: ', () { testCharts(); testFailingBinanceRepository(); diff --git a/test_units/tests/swaps/my_recent_swaps_response_test.dart b/test_units/tests/swaps/my_recent_swaps_response_test.dart new file mode 100644 index 0000000000..bbfa965ab4 --- /dev/null +++ b/test_units/tests/swaps/my_recent_swaps_response_test.dart @@ -0,0 +1,109 @@ +import 'dart:convert'; + +import 'package:test/test.dart'; +import 'package:web_dex/mm2/mm2_api/rpc/my_recent_swaps/my_recent_swaps_response.dart'; + +void testMyRecentSwapsResponse() { + test('parse swap with null my_info and fractions', () { + const payload = ''' +{ + "result": { + "from_uuid": null, + "limit": 1, + "skipped": 0, + "total": 1, + "page_number": 0, + "total_pages": 1, + "found_records": 1, + "swaps": [ + { + "type": "Maker", + "uuid": "uuid1", + "my_order_uuid": "order1", + "events": [], + "maker_amount": "1", + "maker_amount_fraction": null, + "maker_coin": "MCL", + "taker_amount": "2", + "taker_amount_fraction": null, + "taker_coin": "KMD", + "gui": "dex", + "mm_version": "2.0", + "success_events": [], + "error_events": [], + "my_info": null, + "recoverable": false, + "maker_coin_usd_price": null, + "taker_coin_usd_price": null, + "is_finished": true, + "is_success": false + } + ] + } +} +'''; + final Map jsonMap = + jsonDecode(payload) as Map; + final MyRecentSwapsResponse response = MyRecentSwapsResponse.fromJson( + jsonMap, + ); + expect(response.result.fromUuid, isNull); + expect(response.result.swaps.length, 1); + final swap = response.result.swaps.first; + expect(swap.myInfo, isNull); + expect(swap.uuid, 'uuid1'); + }); + + test('parse swap with my_info data', () { + const payload = ''' +{ + "result": { + "from_uuid": "uuid_prev", + "limit": 1, + "skipped": 0, + "total": 1, + "page_number": 0, + "total_pages": 1, + "found_records": 1, + "swaps": [ + { + "type": "Taker", + "uuid": "uuid2", + "my_order_uuid": "order2", + "events": [], + "maker_amount": "3", + "taker_amount": "4", + "maker_coin": "KMD", + "taker_coin": "BTC", + "gui": "dex", + "mm_version": "2.0", + "success_events": [], + "error_events": [], + "my_info": { + "my_coin": "KMD", + "other_coin": "BTC", + "my_amount": "3", + "other_amount": "4", + "started_at": 1 + }, + "recoverable": false + } + ] + } +} +'''; + final Map jsonMap = + jsonDecode(payload) as Map; + final MyRecentSwapsResponse response = MyRecentSwapsResponse.fromJson( + jsonMap, + ); + expect(response.result.fromUuid, 'uuid_prev'); + expect(response.result.swaps.length, 1); + final swap = response.result.swaps.first; + expect(swap.myInfo?.myCoin, 'KMD'); + expect(swap.myInfo?.otherCoin, 'BTC'); + expect(swap.myInfo?.myAmount, 3); + expect(swap.myInfo?.otherAmount, 4); + expect(swap.myInfo?.startedAt, 1); + }); +}