Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf59cf4621 |
@@ -27,6 +27,7 @@ abstract class EmailRepository {
|
||||
Future<EmailBody> getEmailBody(String emailId);
|
||||
Future<SyncEmailsResult> syncEmails(String accountId, String mailboxPath);
|
||||
Future<void> setFlag(String emailId, {bool? seen, bool? flagged});
|
||||
Future<void> markAllAsRead(String accountId, String mailboxPath);
|
||||
Future<void> moveEmail(String emailId, String destMailboxPath);
|
||||
|
||||
/// Deletes the email. Returns the path of the mailbox it was moved to
|
||||
|
||||
@@ -1520,6 +1520,63 @@ class EmailRepositoryImpl implements EmailRepository {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> markAllAsRead(String accountId, String mailboxPath) async {
|
||||
final account = (await _accounts.getAccount(accountId))!;
|
||||
final unread = await (_db.select(_db.emails)
|
||||
..where(
|
||||
(t) =>
|
||||
t.accountId.equals(accountId) &
|
||||
t.mailboxPath.equals(mailboxPath) &
|
||||
t.isSeen.equals(false),
|
||||
))
|
||||
.get();
|
||||
if (unread.isEmpty) return;
|
||||
|
||||
await _db.transaction(() async {
|
||||
for (final row in unread) {
|
||||
if (account.type == account_model.AccountType.jmap) {
|
||||
await _enqueueChange(
|
||||
accountId,
|
||||
row.id,
|
||||
'flag_seen',
|
||||
jsonEncode({'seen': true}),
|
||||
);
|
||||
} else {
|
||||
await _enqueueChange(
|
||||
accountId,
|
||||
row.id,
|
||||
'flag_seen',
|
||||
jsonEncode({
|
||||
'uid': row.uid,
|
||||
'mailboxPath': row.mailboxPath,
|
||||
'seen': true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Bulk mark all unread emails in this mailbox as seen.
|
||||
await (_db.update(_db.emails)
|
||||
..where(
|
||||
(t) =>
|
||||
t.accountId.equals(accountId) &
|
||||
t.mailboxPath.equals(mailboxPath) &
|
||||
t.isSeen.equals(false),
|
||||
))
|
||||
.write(const EmailsCompanion(isSeen: Value(true)));
|
||||
|
||||
// Update all threads in this mailbox to reflect no unread.
|
||||
await (_db.update(_db.threads)
|
||||
..where(
|
||||
(t) =>
|
||||
t.accountId.equals(accountId) &
|
||||
t.mailboxPath.equals(mailboxPath),
|
||||
))
|
||||
.write(const ThreadsCompanion(hasUnread: Value(false)));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> moveEmail(String emailId, String destMailboxPath) async {
|
||||
final row = await (_db.select(
|
||||
|
||||
@@ -193,6 +193,22 @@ class _EmailListScreenState extends ConsumerState<EmailListScreen> {
|
||||
extra: {'accountId': widget.accountId},
|
||||
),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
onSelected: (value) async {
|
||||
if (value == 'mark_all_read') {
|
||||
await emailRepo.markAllAsRead(
|
||||
widget.accountId,
|
||||
widget.mailboxPath,
|
||||
);
|
||||
}
|
||||
},
|
||||
itemBuilder: (_) => const [
|
||||
PopupMenuItem(
|
||||
value: 'mark_all_read',
|
||||
child: Text('Mark all as read'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(60),
|
||||
|
||||
@@ -140,6 +140,9 @@ class _FakeEmails implements EmailRepository {
|
||||
@override
|
||||
Future<void> setFlag(String id, {bool? seen, bool? flagged}) async {}
|
||||
|
||||
@override
|
||||
Future<void> markAllAsRead(String accountId, String mailboxPath) async {}
|
||||
|
||||
@override
|
||||
Future<void> moveEmail(String id, String dest) async {}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ class FakeEmailRepository implements EmailRepository {
|
||||
@override
|
||||
Future<void> setFlag(String id, {bool? seen, bool? flagged}) async {}
|
||||
@override
|
||||
Future<void> markAllAsRead(String accountId, String mailboxPath) async {}
|
||||
@override
|
||||
Future<void> moveEmail(String id, String dest) async {}
|
||||
|
||||
@override
|
||||
|
||||
@@ -215,9 +215,9 @@ class MockEmailRepository extends _i1.Mock implements _i9.EmailRepository {
|
||||
|
||||
@override
|
||||
_i4.Stream<List<_i2.Email>> observeEmails(
|
||||
String accountId,
|
||||
String mailboxPath, {
|
||||
int limit = 50,
|
||||
String? accountId,
|
||||
String? mailboxPath, {
|
||||
int? limit = 50,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -233,9 +233,9 @@ class MockEmailRepository extends _i1.Mock implements _i9.EmailRepository {
|
||||
|
||||
@override
|
||||
_i4.Stream<List<_i2.EmailThread>> observeThreads(
|
||||
String accountId,
|
||||
String mailboxPath, {
|
||||
int limit = 50,
|
||||
String? accountId,
|
||||
String? mailboxPath, {
|
||||
int? limit = 50,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -337,6 +337,23 @@ class MockEmailRepository extends _i1.Mock implements _i9.EmailRepository {
|
||||
returnValueForMissingStub: _i4.Future<void>.value(),
|
||||
) as _i4.Future<void>);
|
||||
|
||||
@override
|
||||
_i4.Future<void> markAllAsRead(
|
||||
String? accountId,
|
||||
String? mailboxPath,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#markAllAsRead,
|
||||
[
|
||||
accountId,
|
||||
mailboxPath,
|
||||
],
|
||||
),
|
||||
returnValue: _i4.Future<void>.value(),
|
||||
returnValueForMissingStub: _i4.Future<void>.value(),
|
||||
) as _i4.Future<void>);
|
||||
|
||||
@override
|
||||
_i4.Future<void> moveEmail(
|
||||
String? emailId,
|
||||
|
||||
@@ -126,6 +126,35 @@ abstract class EmailRepositoryContract {
|
||||
expect(email!.isFlagged, isTrue);
|
||||
});
|
||||
|
||||
test('markAllAsRead marks every unread email in the mailbox', () async {
|
||||
final repo = await makeRepo();
|
||||
await insertEmail(
|
||||
repo,
|
||||
id: 'er-acc:20',
|
||||
mailboxPath: 'INBOX',
|
||||
isSeen: false,
|
||||
);
|
||||
await insertEmail(
|
||||
repo,
|
||||
id: 'er-acc:21',
|
||||
mailboxPath: 'INBOX',
|
||||
isSeen: false,
|
||||
);
|
||||
await insertEmail(
|
||||
repo,
|
||||
id: 'er-acc:22',
|
||||
mailboxPath: 'Sent',
|
||||
isSeen: false,
|
||||
);
|
||||
|
||||
await repo.markAllAsRead(_account.id, 'INBOX');
|
||||
|
||||
expect((await repo.getEmail('er-acc:20'))!.isSeen, isTrue);
|
||||
expect((await repo.getEmail('er-acc:21'))!.isSeen, isTrue);
|
||||
// Email in a different mailbox should be untouched.
|
||||
expect((await repo.getEmail('er-acc:22'))!.isSeen, isFalse);
|
||||
});
|
||||
|
||||
test('observeThreads starts empty', () async {
|
||||
final repo = await makeRepo();
|
||||
expect(
|
||||
|
||||
@@ -103,6 +103,8 @@ class _CountingEmails implements EmailRepository {
|
||||
@override
|
||||
Future<void> setFlag(String id, {bool? seen, bool? flagged}) async {}
|
||||
@override
|
||||
Future<void> markAllAsRead(String accountId, String mailboxPath) async {}
|
||||
@override
|
||||
Future<void> moveEmail(String id, String dest) async {}
|
||||
@override
|
||||
Future<String?> deleteEmail(String id) async => null;
|
||||
|
||||
@@ -75,9 +75,9 @@ class MockEmailRepository extends _i1.Mock implements _i3.EmailRepository {
|
||||
|
||||
@override
|
||||
_i4.Stream<List<_i2.Email>> observeEmails(
|
||||
String accountId,
|
||||
String mailboxPath, {
|
||||
int limit = 50,
|
||||
String? accountId,
|
||||
String? mailboxPath, {
|
||||
int? limit = 50,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -93,9 +93,9 @@ class MockEmailRepository extends _i1.Mock implements _i3.EmailRepository {
|
||||
|
||||
@override
|
||||
_i4.Stream<List<_i2.EmailThread>> observeThreads(
|
||||
String accountId,
|
||||
String mailboxPath, {
|
||||
int limit = 50,
|
||||
String? accountId,
|
||||
String? mailboxPath, {
|
||||
int? limit = 50,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
@@ -197,6 +197,23 @@ class MockEmailRepository extends _i1.Mock implements _i3.EmailRepository {
|
||||
returnValueForMissingStub: _i4.Future<void>.value(),
|
||||
) as _i4.Future<void>);
|
||||
|
||||
@override
|
||||
_i4.Future<void> markAllAsRead(
|
||||
String? accountId,
|
||||
String? mailboxPath,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#markAllAsRead,
|
||||
[
|
||||
accountId,
|
||||
mailboxPath,
|
||||
],
|
||||
),
|
||||
returnValue: _i4.Future<void>.value(),
|
||||
returnValueForMissingStub: _i4.Future<void>.value(),
|
||||
) as _i4.Future<void>);
|
||||
|
||||
@override
|
||||
_i4.Future<void> moveEmail(
|
||||
String? emailId,
|
||||
|
||||
@@ -213,6 +213,8 @@ class FakeEmailRepository implements EmailRepository {
|
||||
|
||||
@override
|
||||
Future<void> setFlag(String emailId, {bool? seen, bool? flagged}) async {}
|
||||
@override
|
||||
Future<void> markAllAsRead(String accountId, String mailboxPath) async {}
|
||||
|
||||
@override
|
||||
Future<void> moveEmail(String emailId, String destMailboxPath) async {}
|
||||
|
||||
Reference in New Issue
Block a user