feat: extend sync log with skipped count and bytes transferred

Track how many emails were already up-to-date (skipped) and the
approximate bytes transferred per sync cycle. SyncEmailsResult
accumulates fetched/skipped/bytes across mailboxes; DB schema v11
adds emailsSkipped and bytesTransferred columns to sync_logs.
SyncLogScreen shows "X new · Y up-to-date · took Zs" in the tile
subtitle with full detail rows in the expansion panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas Güttler
2026-04-21 11:32:50 +02:00
co-authored by Claude Sonnet 4.6
parent 0435129434
commit 1ab915d73a
12 changed files with 156 additions and 34 deletions
@@ -71,7 +71,8 @@ class _FakeEmails implements EmailRepository {
const EmailBody(emailId: '', attachments: []);
@override
Future<int> syncEmails(String a, String m) async => 0;
Future<SyncEmailsResult> syncEmails(String a, String m) async =>
SyncEmailsResult.zero;
@override
Future<void> setFlag(String id, {bool? seen, bool? flagged}) async {}
+15 -5
View File
@@ -71,7 +71,11 @@ class FakeEmailRepository implements EmailRepository {
const EmailBody(emailId: '', attachments: []);
@override
Future<int> syncEmails(String accountId, String mailboxPath) async => 0;
Future<SyncEmailsResult> syncEmails(
String accountId,
String mailboxPath,
) async =>
SyncEmailsResult.zero;
@override
Future<void> setFlag(String emailId, {bool? seen, bool? flagged}) async {}
@@ -157,9 +161,12 @@ class _CountingEmailRepository extends FakeEmailRepository {
final List<String> syncedPaths = [];
@override
Future<int> syncEmails(String accountId, String mailboxPath) async {
Future<SyncEmailsResult> syncEmails(
String accountId,
String mailboxPath,
) async {
syncedPaths.add(mailboxPath);
return 0;
return SyncEmailsResult.zero;
}
}
@@ -167,10 +174,13 @@ class FailingJmapEmailRepository extends FakeEmailRepository {
int syncCount = 0;
@override
Future<int> syncEmails(String accountId, String mailboxPath) async {
Future<SyncEmailsResult> syncEmails(
String accountId,
String mailboxPath,
) async {
syncCount++;
if (syncCount == 1) throw Exception('simulated JMAP failure');
return 0;
return SyncEmailsResult.zero;
}
}
@@ -37,8 +37,10 @@ void main() {
success: true,
protocol: 'imap',
emailsFetched: 5,
emailsSkipped: 0,
mailboxesSynced: 3,
pendingFlushed: 0,
bytesTransferred: 0,
startedAt: start,
finishedAt: end,
);
@@ -65,8 +67,10 @@ void main() {
errorMessage: 'Connection refused',
protocol: 'imap',
emailsFetched: 0,
emailsSkipped: 0,
mailboxesSynced: 0,
pendingFlushed: 0,
bytesTransferred: 0,
startedAt: start,
finishedAt: end,
);
+5 -1
View File
@@ -156,7 +156,11 @@ class FakeEmailRepository implements EmailRepository {
Future<EmailBody> getEmailBody(String emailId) async => _emailBody;
@override
Future<int> syncEmails(String accountId, String mailboxPath) async => 0;
Future<SyncEmailsResult> syncEmails(
String accountId,
String mailboxPath,
) async =>
SyncEmailsResult.zero;
@override
Future<void> setFlag(String emailId, {bool? seen, bool? flagged}) async {}