refactor: remove SettingsScreen, merge into AccountListScreen
The Settings page was a duplicate of the accounts list with less functionality. AccountListScreen now has a "Sync log" entry in each account's popup menu and no longer has a settings icon in the app bar. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
643bb47f87
commit
39cf6b7f6a
@@ -7,7 +7,6 @@ import 'screens/edit_account_screen.dart';
|
||||
import 'screens/email_detail_screen.dart';
|
||||
import 'screens/email_list_screen.dart';
|
||||
import 'screens/mailbox_list_screen.dart';
|
||||
import 'screens/settings_screen.dart';
|
||||
import 'screens/sync_log_screen.dart';
|
||||
|
||||
final router = GoRouter(
|
||||
@@ -71,9 +70,5 @@ final router = GoRouter(
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/settings',
|
||||
builder: (ctx, state) => const SettingsScreen(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -11,15 +11,7 @@ class AccountListScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('SharedInbox'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () => context.push('/settings'),
|
||||
),
|
||||
],
|
||||
),
|
||||
appBar: AppBar(title: const Text('SharedInbox')),
|
||||
body: StreamBuilder(
|
||||
stream: ref.watch(accountRepositoryProvider).observeAccounts(),
|
||||
builder: (ctx, snap) {
|
||||
@@ -104,6 +96,10 @@ class _AccountTile extends ConsumerWidget {
|
||||
value: _AccountAction.allMailboxes,
|
||||
child: Text('All mailboxes'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _AccountAction.syncLog,
|
||||
child: Text('Sync log'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _AccountAction.edit,
|
||||
child: Text('Edit'),
|
||||
@@ -127,6 +123,8 @@ class _AccountTile extends ConsumerWidget {
|
||||
switch (action) {
|
||||
case _AccountAction.allMailboxes:
|
||||
await context.push('/accounts/${account.id}/mailboxes');
|
||||
case _AccountAction.syncLog:
|
||||
await context.push('/accounts/${account.id}/sync-log');
|
||||
case _AccountAction.edit:
|
||||
await context.push('/accounts/${account.id}/edit');
|
||||
case _AccountAction.delete:
|
||||
@@ -158,4 +156,4 @@ class _AccountTile extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
enum _AccountAction { allMailboxes, edit, delete }
|
||||
enum _AccountAction { allMailboxes, syncLog, edit, delete }
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../di.dart';
|
||||
|
||||
class SettingsScreen extends ConsumerWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final repo = ref.watch(accountRepositoryProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Settings')),
|
||||
body: StreamBuilder(
|
||||
stream: repo.observeAccounts(),
|
||||
builder: (ctx, snap) {
|
||||
final accounts = snap.data ?? [];
|
||||
return ListView(
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text(
|
||||
'Accounts',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
for (final a in accounts)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.account_circle),
|
||||
title: Text(a.displayName),
|
||||
subtitle: Text(a.email),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.history),
|
||||
tooltip: 'Sync log',
|
||||
onPressed: () => ctx.push('/accounts/${a.id}/sync-log'),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
final confirm = await showDialog<bool>(
|
||||
context: ctx,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Remove account?'),
|
||||
content: Text(
|
||||
'Remove ${a.displayName}? Local data will be deleted.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx, false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(ctx, true),
|
||||
child: const Text('Remove'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirm == true) {
|
||||
await repo.removeAccount(a.id);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -85,18 +85,6 @@ void main() {
|
||||
expect(find.text('SharedInbox'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('tapping settings icon navigates to /settings', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(initialLocation: '/accounts', overrides: baseOverrides()),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.settings));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Settings'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'"Add account" button in empty state navigates to add-account screen',
|
||||
(tester) async {
|
||||
|
||||
@@ -27,7 +27,6 @@ import 'package:sharedinbox/ui/screens/edit_account_screen.dart';
|
||||
import 'package:sharedinbox/ui/screens/email_detail_screen.dart';
|
||||
import 'package:sharedinbox/ui/screens/email_list_screen.dart';
|
||||
import 'package:sharedinbox/ui/screens/mailbox_list_screen.dart';
|
||||
import 'package:sharedinbox/ui/screens/settings_screen.dart';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fake repositories
|
||||
@@ -298,10 +297,6 @@ Widget buildApp({
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/settings',
|
||||
builder: (ctx, state) => const SettingsScreen(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:sharedinbox/di.dart';
|
||||
|
||||
import 'helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('SettingsScreen', () {
|
||||
testWidgets('shows "Accounts" section header', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/settings',
|
||||
overrides: [
|
||||
accountRepositoryProvider
|
||||
.overrideWithValue(FakeAccountRepository()),
|
||||
mailboxRepositoryProvider
|
||||
.overrideWithValue(FakeMailboxRepository()),
|
||||
emailRepositoryProvider.overrideWithValue(FakeEmailRepository()),
|
||||
],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Accounts'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('shows account tile when an account exists', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/settings',
|
||||
overrides: [
|
||||
accountRepositoryProvider
|
||||
.overrideWithValue(FakeAccountRepository([kTestAccount])),
|
||||
mailboxRepositoryProvider
|
||||
.overrideWithValue(FakeMailboxRepository()),
|
||||
emailRepositoryProvider.overrideWithValue(FakeEmailRepository()),
|
||||
],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Alice'), findsOneWidget);
|
||||
expect(find.text('alice@example.com'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('tapping delete icon shows confirmation dialog',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/settings',
|
||||
overrides: [
|
||||
accountRepositoryProvider
|
||||
.overrideWithValue(FakeAccountRepository([kTestAccount])),
|
||||
mailboxRepositoryProvider
|
||||
.overrideWithValue(FakeMailboxRepository()),
|
||||
emailRepositoryProvider.overrideWithValue(FakeEmailRepository()),
|
||||
],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.delete));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Remove account?'), findsOneWidget);
|
||||
expect(find.text('Cancel'), findsOneWidget);
|
||||
expect(find.text('Remove'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('tapping Remove in the confirmation dialog calls removeAccount',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/settings',
|
||||
overrides: [
|
||||
accountRepositoryProvider
|
||||
.overrideWithValue(FakeAccountRepository([kTestAccount])),
|
||||
mailboxRepositoryProvider
|
||||
.overrideWithValue(FakeMailboxRepository()),
|
||||
emailRepositoryProvider.overrideWithValue(FakeEmailRepository()),
|
||||
],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.delete));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Remove'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Dialog dismissed after confirmation.
|
||||
expect(find.text('Remove account?'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('tapping Cancel in the confirmation dialog dismisses it',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/settings',
|
||||
overrides: [
|
||||
accountRepositoryProvider
|
||||
.overrideWithValue(FakeAccountRepository([kTestAccount])),
|
||||
mailboxRepositoryProvider
|
||||
.overrideWithValue(FakeMailboxRepository()),
|
||||
emailRepositoryProvider.overrideWithValue(FakeEmailRepository()),
|
||||
],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.delete));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Cancel'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Remove account?'), findsNothing);
|
||||
expect(find.text('Alice'), findsOneWidget); // account still visible
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user