mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Keep notification cards collapsed when a sibling is removed
SliverList recycles State objects by index, so when an expanded notification was marked as read and dropped from the filtered list, the next notification inherited the expanded state. Give each card a ValueKey(id) so its expand/collapse state is matched by notification, not by list position. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2a6d480428
commit
aa5a29271b
@@ -309,6 +309,7 @@ class _NotificationsListState extends State<NotificationsList>
|
||||
itemBuilder: (context, index) {
|
||||
final item = _items[index];
|
||||
return NotificationCard(
|
||||
key: ValueKey(item.id),
|
||||
item: item,
|
||||
formatter: formatter,
|
||||
onSetRead: (read) => _setRead(item, read),
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:frontend/home/notifications_list.dart';
|
||||
import 'package:frontend/theme/gruvbox_theme.dart';
|
||||
import 'package:http_mock_adapter/http_mock_adapter.dart';
|
||||
|
||||
import '../helpers/backend_test_harness.dart';
|
||||
import '../helpers/fixtures.dart';
|
||||
|
||||
void main() {
|
||||
late DioAdapter adapter;
|
||||
|
||||
setUp(() async {
|
||||
adapter = await setUpBackendForTest();
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'marking an expanded "New" notification as read does not expand the next',
|
||||
(tester) async {
|
||||
adapter.onGet(
|
||||
'/notifications',
|
||||
(server) => server.reply(200, [
|
||||
notificationJson(id: 1, title: 'First', body: 'First body'),
|
||||
notificationJson(id: 2, title: 'Second', body: 'Second body'),
|
||||
]),
|
||||
queryParameters: {'is_new': true, 'page_offset': 0, 'page_limit': 6},
|
||||
);
|
||||
adapter.onGet('/notifications/1', (server) => server.reply(200, null));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: gruvboxDarkTheme,
|
||||
home: const Scaffold(body: NotificationsList()),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expand the first notification.
|
||||
await tester.tap(find.byType(ListTile).first);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Mark as read'), findsOneWidget);
|
||||
|
||||
// Mark it as read; it disappears from the "New"-filtered list.
|
||||
await tester.tap(find.text('Mark as read'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The first notification is gone and the second stays collapsed: no
|
||||
// card is expanded, so no action buttons are showing.
|
||||
expect(find.textContaining('First'), findsNothing);
|
||||
expect(find.textContaining('Second'), findsWidgets);
|
||||
expect(find.text('Mark as read'), findsNothing);
|
||||
|
||||
// Dispose the widget so its polling timer is cancelled.
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user