- Replace full-sync fetchMessages(1:*) with UID SEARCH ALL + UID FETCH so every message gets a reliable UID on all servers - Guard CONDSTORE select on server capability to avoid BAD from servers that do not advertise CONDSTORE/QRESYNC - Add SyncLogEntry model + observeSyncLogs stream to SyncLogRepository - Add SyncLogScreen with per-entry duration/error display - Wire history icon in SettingsScreen → /accounts/:id/sync-log route - Fix FakeImapClient to expose initialized serverInfo via field override Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
77 lines
2.8 KiB
Dart
77 lines
2.8 KiB
Dart
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);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|