fix: format, analyze-fix and update mocks

This commit is contained in:
Thomas Güttler
2026-06-02 17:10:16 +02:00
parent 3520f161e3
commit 8ea8d71f42
84 changed files with 1972 additions and 2201 deletions
+4 -4
View File
@@ -346,10 +346,10 @@ class SyncEmailsResult {
);
SyncEmailsResult operator +(SyncEmailsResult other) => SyncEmailsResult(
fetched: fetched + other.fetched,
skipped: skipped + other.skipped,
bytesTransferred: bytesTransferred + other.bytesTransferred,
);
fetched: fetched + other.fetched,
skipped: skipped + other.skipped,
bytesTransferred: bytesTransferred + other.bytesTransferred,
);
}
class ReliabilityResult {
@@ -35,9 +35,8 @@ class AccountDiscoveryServiceImpl implements AccountDiscoveryService {
try {
final url = Uri.https(domain, '/.well-known/jmap');
final request = http.Request('GET', url)..followRedirects = false;
final streamed = await _client
.send(request)
.timeout(const Duration(seconds: 5));
final streamed =
await _client.send(request).timeout(const Duration(seconds: 5));
String sessionUrl;
if (streamed.statusCode >= 300 && streamed.statusCode < 400) {
+26 -17
View File
@@ -6,24 +6,30 @@ import 'package:sharedinbox/core/models/account.dart';
import 'package:sharedinbox/data/imap/imap_client_factory.dart';
import 'package:sharedinbox/data/imap/managesieve_client.dart';
typedef ImapConnectForTestFn =
Future<imap.ImapClient> Function(Account, String username, String password);
typedef ImapConnectForTestFn = Future<imap.ImapClient> Function(
Account,
String username,
String password,
);
typedef SmtpConnectForTestFn =
Future<imap.SmtpClient> Function(Account, String username, String password);
typedef SmtpConnectForTestFn = Future<imap.SmtpClient> Function(
Account,
String username,
String password,
);
typedef ManageSieveConnectForTestFn =
Future<ManageSieveClient> Function({
required String host,
required int port,
required bool useTls,
});
typedef ManageSieveConnectForTestFn = Future<ManageSieveClient> Function({
required String host,
required int port,
required bool useTls,
});
Future<ManageSieveClient> _defaultManageSieveConnect({
required String host,
required int port,
required bool useTls,
}) => ManageSieveClient.connect(host: host, port: port, useTls: useTls);
}) =>
ManageSieveClient.connect(host: host, port: port, useTls: useTls);
abstract class ConnectionTestService {
/// Verifies credentials and returns the effective username used.
@@ -37,9 +43,9 @@ class ConnectionTestServiceImpl implements ConnectionTestService {
ImapConnectForTestFn imapConnect = connectImap,
SmtpConnectForTestFn smtpConnect = connectSmtp,
ManageSieveConnectForTestFn manageSieveConnect = _defaultManageSieveConnect,
}) : _imapConnect = imapConnect,
_smtpConnect = smtpConnect,
_manageSieveConnect = manageSieveConnect;
}) : _imapConnect = imapConnect,
_smtpConnect = smtpConnect,
_manageSieveConnect = manageSieveConnect;
final http.Client _httpClient;
final ImapConnectForTestFn _imapConnect;
@@ -156,9 +162,12 @@ class ConnectionTestServiceImpl implements ConnectionTestService {
for (final username in candidates) {
try {
final credentials = base64.encode(utf8.encode('$username:$password'));
final resp = await _httpClient
.get(sessionUri, headers: {'Authorization': 'Basic $credentials'})
.timeout(const Duration(seconds: 10));
final resp = await _httpClient.get(
sessionUri,
headers: {
'Authorization': 'Basic $credentials',
},
).timeout(const Duration(seconds: 10));
if (resp.statusCode == 401 || resp.statusCode == 403) {
lastError = Exception(
'Authentication failed: wrong username or password',
@@ -4,12 +4,11 @@ import 'package:sharedinbox/core/utils/logger.dart';
import 'package:sharedinbox/data/imap/managesieve_client.dart';
/// Returns true if the endpoint accepts a ManageSieve handshake.
typedef ManageSieveProbeFn =
Future<bool> Function({
required String host,
required int port,
required bool useTls,
});
typedef ManageSieveProbeFn = Future<bool> Function({
required String host,
required int port,
required bool useTls,
});
Future<bool> _defaultManageSieveProbe({
required String host,
@@ -66,22 +65,22 @@ class ManageSieveProbeService {
}
Account _withAvailability(Account a, bool available) => Account(
id: a.id,
displayName: a.displayName,
email: a.email,
username: a.username,
type: a.type,
imapHost: a.imapHost,
imapPort: a.imapPort,
imapSsl: a.imapSsl,
smtpHost: a.smtpHost,
smtpPort: a.smtpPort,
smtpSsl: a.smtpSsl,
manageSieveHost: a.manageSieveHost,
manageSievePort: a.manageSievePort,
manageSieveSsl: a.manageSieveSsl,
manageSieveAvailable: available,
jmapUrl: a.jmapUrl,
verbose: a.verbose,
);
id: a.id,
displayName: a.displayName,
email: a.email,
username: a.username,
type: a.type,
imapHost: a.imapHost,
imapPort: a.imapPort,
imapSsl: a.imapSsl,
smtpHost: a.smtpHost,
smtpPort: a.smtpPort,
smtpSsl: a.smtpSsl,
manageSieveHost: a.manageSieveHost,
manageSievePort: a.manageSievePort,
manageSieveSsl: a.manageSieveSsl,
manageSieveAvailable: available,
jmapUrl: a.jmapUrl,
verbose: a.verbose,
);
}
+1 -2
View File
@@ -18,8 +18,7 @@ Future<void> initNotifications() async {
);
await _plugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
_initialized = true;
} on MissingPluginException {
+11 -12
View File
@@ -166,18 +166,17 @@ class ShareEncryptionService {
final cipherBytes = Uint8List.fromList(box.cipherText);
final macBytes = Uint8List.fromList(box.mac.bytes);
final out =
Uint8List(
_keyIdLen + _pubKeyLen + _nonceLen + cipherBytes.length + _macLen,
)
..setAll(0, recipientKeyId)
..setAll(_keyIdLen, ephPubBytes)
..setAll(_keyIdLen + _pubKeyLen, nonce)
..setAll(_keyIdLen + _pubKeyLen + _nonceLen, cipherBytes)
..setAll(
_keyIdLen + _pubKeyLen + _nonceLen + cipherBytes.length,
macBytes,
);
final out = Uint8List(
_keyIdLen + _pubKeyLen + _nonceLen + cipherBytes.length + _macLen,
)
..setAll(0, recipientKeyId)
..setAll(_keyIdLen, ephPubBytes)
..setAll(_keyIdLen + _pubKeyLen, nonce)
..setAll(_keyIdLen + _pubKeyLen + _nonceLen, cipherBytes)
..setAll(
_keyIdLen + _pubKeyLen + _nonceLen + cipherBytes.length,
macBytes,
);
return '$_encAccountsPrefix${base64.encode(out)}';
}
+1 -2
View File
@@ -62,8 +62,7 @@ class UndoService extends Notifier<List<UndoAction>> {
for (final id in action.emailIds) {
// 1. Try to cancel the original change (if not started yet).
final cancelled =
await repo.cancelPendingChange(id, 'delete') ||
final cancelled = await repo.cancelPendingChange(id, 'delete') ||
await repo.cancelPendingChange(id, 'move') ||
await repo.cancelPendingChange(id, 'snooze');
+2 -2
View File
@@ -21,8 +21,8 @@ final updateInfoProvider = FutureProvider<UpdateInfo?>((ref) async {
final platformKey = Platform.isLinux
? 'linux'
: Platform.isWindows
? 'windows'
: null;
? 'windows'
: null;
if (platformKey == null || _kAppVersion.isEmpty) return null;
try {
+2 -3
View File
@@ -64,9 +64,8 @@ class SieveInterpreter {
return switch (rule.joinType) {
'allof' => rule.conditions.every((c) => _evalCondition(c, email)),
'anyof' => rule.conditions.any((c) => _evalCondition(c, email)),
_ =>
rule.conditions.length == 1 &&
_evalCondition(rule.conditions.first, email),
_ => rule.conditions.length == 1 &&
_evalCondition(rule.conditions.first, email),
};
}
+2 -2
View File
@@ -421,8 +421,8 @@ class _Scanner {
if (_isWordChar(ch)) {
final start = _pos;
var end = _pos + 1;
while (end < _src.length &&
(_isWordChar(_src[end]) || _src[end] == ':')) {
while (
end < _src.length && (_isWordChar(_src[end]) || _src[end] == ':')) {
// Include trailing colon for "text:" multiline token.
if (_src[end] == ':') {
end++;
+64 -68
View File
@@ -29,10 +29,10 @@ class AccountSyncManager {
SyncLogRepository syncLog = const NoOpSyncLogRepository(),
DraftRepository? drafts,
OnNewMailCallback? onNewMail,
}) : _imapConnect = imapConnect,
_syncLog = syncLog,
_drafts = drafts,
_onNewMail = onNewMail;
}) : _imapConnect = imapConnect,
_syncLog = syncLog,
_drafts = drafts,
_onNewMail = onNewMail;
final AccountRepository _accounts;
final MailboxRepository _mailboxes;
@@ -69,26 +69,26 @@ class AccountSyncManager {
final id = account.id;
final loop = switch (account.type) {
AccountType.imap => _AccountSync(
account,
_accounts,
_mailboxes,
_emails,
_imapConnect,
_syncLog,
_drafts,
_onNewMail,
onSyncStart: () => _emitSyncing(id, syncing: true),
onSyncEnd: () => _emitSyncing(id, syncing: false),
),
account,
_accounts,
_mailboxes,
_emails,
_imapConnect,
_syncLog,
_drafts,
_onNewMail,
onSyncStart: () => _emitSyncing(id, syncing: true),
onSyncEnd: () => _emitSyncing(id, syncing: false),
),
AccountType.jmap => _JmapAccountSync(
account,
_mailboxes,
_emails,
_accounts,
_syncLog,
onSyncStart: () => _emitSyncing(id, syncing: true),
onSyncEnd: () => _emitSyncing(id, syncing: false),
),
account,
_mailboxes,
_emails,
_accounts,
_syncLog,
onSyncStart: () => _emitSyncing(id, syncing: true),
onSyncEnd: () => _emitSyncing(id, syncing: false),
),
};
_active[account.id] = loop;
loop.start();
@@ -129,33 +129,33 @@ class AccountSyncManager {
final accounts = await _accounts.observeAccounts().first;
final account = accounts.cast<Account?>().firstWhere(
(a) => a?.id == accountId,
orElse: () => null,
);
(a) => a?.id == accountId,
orElse: () => null,
);
if (account == null) return;
final loop = switch (account.type) {
AccountType.imap => _AccountSync(
account,
_accounts,
_mailboxes,
_emails,
_imapConnect,
_syncLog,
_drafts,
_onNewMail,
onSyncStart: () => _emitSyncing(accountId, syncing: true),
onSyncEnd: () => _emitSyncing(accountId, syncing: false),
),
account,
_accounts,
_mailboxes,
_emails,
_imapConnect,
_syncLog,
_drafts,
_onNewMail,
onSyncStart: () => _emitSyncing(accountId, syncing: true),
onSyncEnd: () => _emitSyncing(accountId, syncing: false),
),
AccountType.jmap => _JmapAccountSync(
account,
_mailboxes,
_emails,
_accounts,
_syncLog,
onSyncStart: () => _emitSyncing(accountId, syncing: true),
onSyncEnd: () => _emitSyncing(accountId, syncing: false),
),
account,
_mailboxes,
_emails,
_accounts,
_syncLog,
onSyncStart: () => _emitSyncing(accountId, syncing: true),
onSyncEnd: () => _emitSyncing(accountId, syncing: false),
),
};
_active[accountId] = loop;
loop.start();
@@ -184,8 +184,8 @@ class _AccountSync implements _SyncLoop {
this._onNewMail, {
void Function()? onSyncStart,
void Function()? onSyncEnd,
}) : _onSyncStart = onSyncStart,
_onSyncEnd = onSyncEnd;
}) : _onSyncStart = onSyncStart,
_onSyncEnd = onSyncEnd;
final Account account;
final AccountRepository _accounts;
@@ -379,9 +379,8 @@ class _AccountSync implements _SyncLoop {
if (!_running) return;
_stopSignal = Completer<void>();
final password = await _accounts.getPassword(account.id);
final username = account.username.isNotEmpty
? account.username
: account.email;
final username =
account.username.isNotEmpty ? account.username : account.email;
final client = await _imapConnect(account, username, password);
_idleClient = client;
try {
@@ -397,13 +396,12 @@ class _AccountSync implements _SyncLoop {
e is imap.ImapMessagesExistEvent || e is imap.ImapExpungeEvent,
)
.listen((e) {
if (e is imap.ImapMessagesExistEvent &&
e.newMessagesExists > e.oldMessagesExists) {
hasNewMail = true;
}
if (!newMessageCompleter.isCompleted)
newMessageCompleter.complete();
});
if (e is imap.ImapMessagesExistEvent &&
e.newMessagesExists > e.oldMessagesExists) {
hasNewMail = true;
}
if (!newMessageCompleter.isCompleted) newMessageCompleter.complete();
});
await client.idleStart();
@@ -445,8 +443,8 @@ class _JmapAccountSync implements _SyncLoop {
this._syncLog, {
void Function()? onSyncStart,
void Function()? onSyncEnd,
}) : _onSyncStart = onSyncStart,
_onSyncEnd = onSyncEnd;
}) : _onSyncStart = onSyncStart,
_onSyncEnd = onSyncEnd;
final Account account;
final MailboxRepository _mailboxes;
@@ -642,15 +640,13 @@ class _JmapAccountSync implements _SyncLoop {
// Try JMAP push (RFC 8887 EventSource). Falls back to poll timer when
// the server doesn't advertise an eventSourceUrl or the connection fails.
final pushReady = Completer<void>();
final pushSub = _emails
.watchJmapPush(account.id, password)
.listen(
(_) {
if (!pushReady.isCompleted) pushReady.complete();
},
onDone: () {},
onError: (_) {},
);
final pushSub = _emails.watchJmapPush(account.id, password).listen(
(_) {
if (!pushReady.isCompleted) pushReady.complete();
},
onDone: () {},
onError: (_) {},
);
final pollTimer = Timer(_pollInterval, () {
if (_stopSignal != null && !_stopSignal!.isCompleted) {
+10 -13
View File
@@ -83,9 +83,8 @@ Future<void> _checkAccount(
) async {
try {
final password = await accountRepo.getPassword(account.id);
final username = account.username.isNotEmpty
? account.username
: account.email;
final username =
account.username.isNotEmpty ? account.username : account.email;
final client = await connectImap(account, username, password);
try {
final status = await client.statusMailbox(
@@ -94,18 +93,16 @@ Future<void> _checkAccount(
);
final currentUidNext = status.uidNext;
final stored =
await (db.select(db.syncStates)..where(
(t) =>
t.accountId.equals(account.id) &
t.resourceType.equals(_kResourceType),
))
.getSingleOrNull();
final stored = await (db.select(db.syncStates)
..where(
(t) =>
t.accountId.equals(account.id) &
t.resourceType.equals(_kResourceType),
))
.getSingleOrNull();
final lastUidNext = _parseUidNext(stored?.state);
await db
.into(db.syncStates)
.insertOnConflictUpdate(
await db.into(db.syncStates).insertOnConflictUpdate(
SyncStatesCompanion.insert(
accountId: account.id,
resourceType: _kResourceType,
+2 -5
View File
@@ -76,14 +76,11 @@ class ReliabilityRunner {
}
}
final isHealthy =
totalMissingLocally == 0 &&
final isHealthy = totalMissingLocally == 0 &&
totalMissingOnServer == 0 &&
totalFlagMismatches == 0;
await _db
.into(_db.syncHealth)
.insertOnConflictUpdate(
await _db.into(_db.syncHealth).insertOnConflictUpdate(
SyncHealthCompanion.insert(
accountId: accountId,
lastVerifiedAt: DateTime.now(),