fix: do not show snackbar for stale undo actions on startup (#113)
Actions persisted to the database triggered a snackbar when the app restarted. Added a 30-second recency check so only actions created in the current session show the snackbar; added widget tests covering both cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
0611323cfa
commit
651110b389
@@ -13,7 +13,12 @@ class UndoShell extends ConsumerWidget {
|
||||
ref.listen<List<UndoAction>>(undoServiceProvider, (previous, next) {
|
||||
if (next.isNotEmpty &&
|
||||
(previous == null || previous.length < next.length)) {
|
||||
_showUndoSnackbar(context, ref, next.last);
|
||||
final action = next.last;
|
||||
// Don't show a snackbar for actions loaded from persistence on app
|
||||
// startup — only for actions pushed in this session.
|
||||
if (DateTime.now().difference(action.timestamp).inSeconds < 30) {
|
||||
_showUndoSnackbar(context, ref, action);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:sharedinbox/core/models/undo_action.dart';
|
||||
import 'package:sharedinbox/di.dart';
|
||||
import 'package:sharedinbox/ui/widgets/undo_shell.dart';
|
||||
|
||||
import '../unit/undo_service_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
late MockUndoRepository mockUndoRepo;
|
||||
|
||||
setUp(() {
|
||||
mockUndoRepo = MockUndoRepository();
|
||||
when(mockUndoRepo.saveAction(any)).thenAnswer((_) async {});
|
||||
when(mockUndoRepo.deleteAction(any)).thenAnswer((_) async {});
|
||||
when(mockUndoRepo.clearHistory()).thenAnswer((_) async {});
|
||||
});
|
||||
|
||||
Widget buildShell(MockUndoRepository repo) {
|
||||
return ProviderScope(
|
||||
overrides: [undoRepositoryProvider.overrideWithValue(repo)],
|
||||
child: const MaterialApp(
|
||||
home: UndoShell(child: Scaffold(body: Text('content'))),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets(
|
||||
'does not show snackbar for stale action loaded from persistence on startup',
|
||||
(tester) async {
|
||||
final staleAction = UndoAction(
|
||||
id: '1',
|
||||
accountId: 'acc1',
|
||||
type: UndoType.move,
|
||||
emailIds: ['e1'],
|
||||
sourceMailboxPath: 'INBOX',
|
||||
timestamp: DateTime.now().subtract(const Duration(hours: 1)),
|
||||
);
|
||||
when(mockUndoRepo.getHistory(limit: anyNamed('limit')))
|
||||
.thenAnswer((_) async => [staleAction]);
|
||||
|
||||
await tester.pumpWidget(buildShell(mockUndoRepo));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('1 email(s) moved'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('shows snackbar for fresh action pushed in current session',
|
||||
(tester) async {
|
||||
when(mockUndoRepo.getHistory(limit: anyNamed('limit')))
|
||||
.thenAnswer((_) async => []);
|
||||
|
||||
await tester.pumpWidget(buildShell(mockUndoRepo));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final context = tester.element(find.byType(UndoShell));
|
||||
final freshAction = UndoAction(
|
||||
id: '1',
|
||||
accountId: 'acc1',
|
||||
type: UndoType.move,
|
||||
emailIds: ['e1'],
|
||||
sourceMailboxPath: 'INBOX',
|
||||
);
|
||||
await ProviderScope.containerOf(context)
|
||||
.read(undoServiceProvider.notifier)
|
||||
.pushAction(freshAction);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('1 email(s) moved'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows correct text for delete action (moved to Trash)',
|
||||
(tester) async {
|
||||
when(mockUndoRepo.getHistory(limit: anyNamed('limit')))
|
||||
.thenAnswer((_) async => []);
|
||||
|
||||
await tester.pumpWidget(buildShell(mockUndoRepo));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final context = tester.element(find.byType(UndoShell));
|
||||
final deleteAction = UndoAction(
|
||||
id: '2',
|
||||
accountId: 'acc1',
|
||||
type: UndoType.delete,
|
||||
emailIds: ['e1', 'e2'],
|
||||
sourceMailboxPath: 'INBOX',
|
||||
);
|
||||
await ProviderScope.containerOf(context)
|
||||
.read(undoServiceProvider.notifier)
|
||||
.pushAction(deleteAction);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('2 email(s) moved to Trash'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user