From 11d9a33473922ac9fcfef5457b15ca912833cf16 Mon Sep 17 00:00:00 2001 From: joshuakrueger-dfx Date: Wed, 3 Jun 2026 22:15:19 +0200 Subject: [PATCH] fix(tx-history): keep both date bounds when only one filter date changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changeFilter() forwarded the raw partial picker argument straight into _applyFilter, so selecting only a start date (or only an end date) passed null for the other bound and silently dropped the previously-set one from the filtered result — the visible list lost a constraint the state still held. Resolve each bound by merging the partial argument with the bound already in state and use the merged values for both the emitted state and the filtered list. Regression: test/screens/transaction_history/.../transaction_history_filter_cubit_test.dart ("keeps the previously-set bound when only the other changes") Issue #657 — Part 3, finding #1 (HIGH). --- .../transaction_history_filter_cubit.dart | 14 +++++++--- ...transaction_history_filter_cubit_test.dart | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/lib/screens/transaction_history/cubits/filter/transaction_history_filter_cubit.dart b/lib/screens/transaction_history/cubits/filter/transaction_history_filter_cubit.dart index fbe743f1..69784c49 100644 --- a/lib/screens/transaction_history/cubits/filter/transaction_history_filter_cubit.dart +++ b/lib/screens/transaction_history/cubits/filter/transaction_history_filter_cubit.dart @@ -37,14 +37,20 @@ class TransactionHistoryFilterCubit extends Cubit } void changeFilter({DateTime? startDate, DateTime? endDate}) { + // Each date picker reports only the bound it changed, so resolve the + // effective window by merging the partial argument with the bound already + // in state. Using the merged values for BOTH the state and the filtered + // result keeps the other bound from being silently dropped (issue #657 P3). + final effectiveStartDate = startDate ?? state.startDate; + final effectiveEndDate = endDate ?? state.endDate; emit( state.copyWith( - startDate: startDate, - endDate: endDate, + startDate: effectiveStartDate, + endDate: effectiveEndDate, filtered: _applyFilter( state.all, - startDate: startDate, - endDate: endDate, + startDate: effectiveStartDate, + endDate: effectiveEndDate, ), ), ); diff --git a/test/screens/transaction_history/cubits/transaction_history_filter_cubit_test.dart b/test/screens/transaction_history/cubits/transaction_history_filter_cubit_test.dart index f73e82e9..0fe8a3f7 100644 --- a/test/screens/transaction_history/cubits/transaction_history_filter_cubit_test.dart +++ b/test/screens/transaction_history/cubits/transaction_history_filter_cubit_test.dart @@ -128,6 +128,32 @@ void main() { }, ); + blocTest( + 'changeFilter keeps the previously-set bound when only the other changes ' + '(issue #657 P3 regression)', + build: build, + act: (cubit) async { + stream.add([ + _tx(DateTime(2026, 1, 1)), + _tx(DateTime(2026, 3, 1)), + _tx(DateTime(2026, 5, 1)), + ]); + await Future.delayed(Duration.zero); + + // Two pickers, two separate partial updates — start first, then end. + cubit.changeFilter(startDate: DateTime(2026, 2, 1)); + cubit.changeFilter(endDate: DateTime(2026, 4, 1)); + }, + verify: (cubit) { + // Both bounds must survive: the window [Feb 1, Apr 1] leaves only Mar 1. + // Before the fix the second call dropped the start bound, leaking Jan 1. + expect(cubit.state.startDate, DateTime(2026, 2, 1)); + expect(cubit.state.endDate, DateTime(2026, 4, 1)); + expect(cubit.state.filtered, hasLength(1)); + expect(cubit.state.filtered.single.timestamp, DateTime(2026, 3, 1)); + }, + ); + test('close cancels the stream subscription (no emit after close)', () async { final cubit = build(); await cubit.close();