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:
Thomas Güttler
2026-04-21 16:42:05 +02:00
co-authored by Claude Sonnet 4.6
parent 643bb47f87
commit 39cf6b7f6a
6 changed files with 8 additions and 229 deletions
-5
View File
@@ -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(),
),
],
);
+8 -10
View File
@@ -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 }
-76
View File
@@ -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);
}
},
),
],
),
),
],
);
},
),
);
}
}
-12
View File
@@ -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 {
-5
View File
@@ -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(),
),
],
);
-121
View File
@@ -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
});
});
}