fix: format, analyze-fix and update mocks
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ Future<void> initNotifications() async {
|
||||
);
|
||||
await _plugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestNotificationsPermission();
|
||||
_initialized = true;
|
||||
} on MissingPluginException {
|
||||
|
||||
@@ -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)}';
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user