TF-1715 Load emails in favorite folder without filters

This commit is contained in:
dab246
2025-10-30 11:57:22 +07:00
committed by Dat H. Pham
parent d85e691d98
commit e5e1d02ae9
7 changed files with 124 additions and 33 deletions
@@ -134,6 +134,7 @@ class MailboxController extends BaseMailboxController
WebSocketQueueHandler? _webSocketQueueHandler; WebSocketQueueHandler? _webSocketQueueHandler;
final _openMailboxEventController = StreamController<OpenMailboxViewEvent>(); final _openMailboxEventController = StreamController<OpenMailboxViewEvent>();
StreamSubscription? _openMailboxEventStreamSubscription;
final mailboxListScrollController = ScrollController(); final mailboxListScrollController = ScrollController();
PresentationMailbox? get selectedMailbox => mailboxDashBoardController.selectedMailbox.value; PresentationMailbox? get selectedMailbox => mailboxDashBoardController.selectedMailbox.value;
@@ -177,10 +178,13 @@ class MailboxController extends BaseMailboxController
@override @override
void onReady() { void onReady() {
_openMailboxEventController.stream.debounceTime(const Duration(milliseconds: 500)).listen((event) { _openMailboxEventStreamSubscription = _openMailboxEventController
if (!event.buildContext.mounted) return; .stream
_handleOpenMailbox(event.buildContext, event.presentationMailbox); .debounceTime(const Duration(milliseconds: 500))
}); .listen((event) {
if (!event.buildContext.mounted) return;
_handleOpenMailbox(event.buildContext, event.presentationMailbox);
});
_initCollapseMailboxCategories(); _initCollapseMailboxCategories();
mailboxListScrollController.addListener(_mailboxListScrollControllerListener); mailboxListScrollController.addListener(_mailboxListScrollControllerListener);
super.onReady(); super.onReady();
@@ -188,6 +192,8 @@ class MailboxController extends BaseMailboxController
@override @override
void onClose() { void onClose() {
_openMailboxEventStreamSubscription?.cancel();
_openMailboxEventStreamSubscription = null;
_openMailboxEventController.close(); _openMailboxEventController.close();
mailboxListScrollController.dispose(); mailboxListScrollController.dispose();
_webSocketQueueHandler?.dispose(); _webSocketQueueHandler?.dispose();
@@ -314,12 +314,14 @@ class ThreadRepositoryImpl extends ThreadRepository {
@override @override
Stream<EmailsResponse> loadMoreEmails(GetEmailRequest emailRequest) async* { Stream<EmailsResponse> loadMoreEmails(GetEmailRequest emailRequest) async* {
final response = await _getAllEmailsWithoutLastEmailId(emailRequest); final response = await _getAllEmailsWithoutLastEmailId(emailRequest);
await _updateEmailCache( if (emailRequest.useCache) {
emailRequest.accountId, await _updateEmailCache(
emailRequest.session.username, emailRequest.accountId,
newCreated: response.emailList, emailRequest.session.username,
newDestroyed: response.notFoundEmailIds, newCreated: response.emailList,
); newDestroyed: response.notFoundEmailIds,
);
}
yield response; yield response;
} }
@@ -498,4 +500,27 @@ class ThreadRepositoryImpl extends ThreadRepository {
accountId, accountId,
session, session,
); );
@override
Stream<EmailsResponse> loadAllEmailInFolderWithoutCache({
required Session session,
required AccountId accountId,
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
EmailFilter? emailFilter,
Properties? propertiesCreated,
}) async* {
final networkDataSource = mapDataSource[DataSourceType.network]!;
final emailResponse = await networkDataSource.getAllEmail(
session,
accountId,
limit: limit,
position: position,
sort: sort,
filter: emailFilter?.filter,
properties: propertiesCreated,
);
yield emailResponse;
}
} }
@@ -18,6 +18,7 @@ class GetEmailRequest with EquatableMixin {
final FilterMessageOption? filterOption; final FilterMessageOption? filterOption;
final Properties? properties; final Properties? properties;
final EmailId? lastEmailId; final EmailId? lastEmailId;
final bool useCache;
GetEmailRequest( GetEmailRequest(
this.session, this.session,
@@ -30,6 +31,7 @@ class GetEmailRequest with EquatableMixin {
this.filterOption, this.filterOption,
this.properties, this.properties,
this.lastEmailId, this.lastEmailId,
this.useCache = true,
} }
); );
@@ -43,6 +45,7 @@ class GetEmailRequest with EquatableMixin {
filter, filter,
properties, properties,
lastEmailId, lastEmailId,
filterOption filterOption,
useCache,
]; ];
} }
@@ -24,6 +24,7 @@ abstract class ThreadRepository {
AccountId accountId, AccountId accountId,
{ {
UnsignedInt? limit, UnsignedInt? limit,
int? position,
Set<Comparator>? sort, Set<Comparator>? sort,
EmailFilter? emailFilter, EmailFilter? emailFilter,
Properties? propertiesCreated, Properties? propertiesCreated,
@@ -32,6 +33,16 @@ abstract class ThreadRepository {
} }
); );
Stream<EmailsResponse> loadAllEmailInFolderWithoutCache({
required Session session,
required AccountId accountId,
UnsignedInt? limit,
int? position,
Set<Comparator>? sort,
EmailFilter? emailFilter,
Properties? propertiesCreated,
});
Stream<EmailsResponse> refreshChanges( Stream<EmailsResponse> refreshChanges(
Session session, Session session,
AccountId accountId, AccountId accountId,
@@ -27,13 +27,14 @@ class GetEmailsInMailboxInteractor {
Properties? propertiesCreated, Properties? propertiesCreated,
Properties? propertiesUpdated, Properties? propertiesUpdated,
bool getLatestChanges = true, bool getLatestChanges = true,
bool useCache = true,
} }
) async* { ) async* {
try { try {
yield Right<Failure, Success>(GetAllEmailLoading()); yield Right<Failure, Success>(GetAllEmailLoading());
yield* threadRepository if (useCache) {
.getAllEmail( yield* threadRepository.getAllEmail(
session, session,
accountId, accountId,
limit: limit, limit: limit,
@@ -41,11 +42,24 @@ class GetEmailsInMailboxInteractor {
emailFilter: emailFilter, emailFilter: emailFilter,
propertiesCreated: propertiesCreated, propertiesCreated: propertiesCreated,
propertiesUpdated: propertiesUpdated, propertiesUpdated: propertiesUpdated,
getLatestChanges: getLatestChanges) getLatestChanges: getLatestChanges,
.map((emailResponse) => _toGetEmailState( ).map((emailResponse) => _toGetEmailState(
emailResponse: emailResponse, emailResponse: emailResponse,
currentMailboxId: emailFilter?.mailboxId currentMailboxId: emailFilter?.mailboxId,
)); ));
} else {
yield* threadRepository.loadAllEmailInFolderWithoutCache(
session: session,
accountId: accountId,
limit: limit,
sort: sort,
emailFilter: emailFilter,
propertiesCreated: propertiesCreated,
).map((emailResponse) => _toGetEmailState(
emailResponse: emailResponse,
currentMailboxId: emailFilter?.mailboxId,
));
}
} catch (e) { } catch (e) {
yield Left(GetAllEmailFailure(e)); yield Left(GetAllEmailFailure(e));
} }
@@ -9,6 +9,7 @@ import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/filter/filter.dart' show Filter;
import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
@@ -165,7 +166,6 @@ class ThreadController extends BaseController with EmailActionController {
@override @override
void handleSuccessViewState(Success success) { void handleSuccessViewState(Success success) {
super.handleSuccessViewState(success);
if (success is GetAllEmailSuccess) { if (success is GetAllEmailSuccess) {
_getAllEmailSuccess(success); _getAllEmailSuccess(success);
} else if (success is LoadMoreEmailsSuccess) { } else if (success is LoadMoreEmailsSuccess) {
@@ -192,12 +192,13 @@ class ThreadController extends BaseController with EmailActionController {
_openEmailWithoutMailboxFromLocationBar(success.email); _openEmailWithoutMailboxFromLocationBar(success.email);
} }
} }
} else {
super.handleSuccessViewState(success);
} }
} }
@override @override
void handleFailureViewState(Failure failure) { void handleFailureViewState(Failure failure) {
super.handleFailureViewState(failure);
if (failure is SearchEmailFailure) { if (failure is SearchEmailFailure) {
mailboxDashBoardController.updateRefreshAllEmailState(Left(RefreshAllEmailFailure())); mailboxDashBoardController.updateRefreshAllEmailState(Left(RefreshAllEmailFailure()));
canSearchMore = false; canSearchMore = false;
@@ -215,6 +216,8 @@ class ThreadController extends BaseController with EmailActionController {
} else if (failure is GetAllEmailFailure || failure is CleanAndGetAllEmailFailure) { } else if (failure is GetAllEmailFailure || failure is CleanAndGetAllEmailFailure) {
mailboxDashBoardController.updateRefreshAllEmailState(Left(RefreshAllEmailFailure())); mailboxDashBoardController.updateRefreshAllEmailState(Left(RefreshAllEmailFailure()));
canLoadMore = true; canLoadMore = true;
} else {
super.handleFailureViewState(failure);
} }
} }
@@ -503,7 +506,8 @@ class ThreadController extends BaseController with EmailActionController {
void _getAllEmailSuccess(GetAllEmailSuccess success) { void _getAllEmailSuccess(GetAllEmailSuccess success) {
mailboxDashBoardController.updateRefreshAllEmailState(Right(RefreshAllEmailSuccess())); mailboxDashBoardController.updateRefreshAllEmailState(Right(RefreshAllEmailSuccess()));
if (success.currentMailboxId != selectedMailboxId) { if (success.currentMailboxId != selectedMailboxId &&
selectedMailboxId?.isNotFavoriteMailboxId == true) {
log('ThreadController::_getAllEmailSuccess: GetAllForMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}'); log('ThreadController::_getAllEmailSuccess: GetAllForMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}');
return; return;
} }
@@ -543,7 +547,8 @@ class ThreadController extends BaseController with EmailActionController {
} }
void _refreshChangesAllEmailSuccess(RefreshChangesAllEmailSuccess success) { void _refreshChangesAllEmailSuccess(RefreshChangesAllEmailSuccess success) {
if (success.currentMailboxId != selectedMailboxId) { if (success.currentMailboxId != selectedMailboxId &&
selectedMailboxId?.isNotFavoriteMailboxId == true) {
log('ThreadController::_refreshChangesAllEmailSuccess: RefreshedMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}'); log('ThreadController::_refreshChangesAllEmailSuccess: RefreshedMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}');
return; return;
} }
@@ -579,23 +584,49 @@ class ThreadController extends BaseController with EmailActionController {
_accountId!, _accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(), sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
emailFilter: EmailFilter( emailFilter: _getEmailFilterForLoadMailbox(),
filter: getFilterCondition(mailboxIdSelected: selectedMailboxId),
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: selectedMailboxId
),
propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
propertiesUpdated: EmailUtils.getPropertiesForEmailChangeMethod( propertiesUpdated: EmailUtils.getPropertiesForEmailChangeMethod(
_session!, _session!,
_accountId!, _accountId!,
), ),
getLatestChanges: getLatestChanges, getLatestChanges: getLatestChanges,
useCache: selectedMailboxId?.isNotFavoriteMailboxId == true,
)); ));
} else { } else {
consumeState(Stream.value(Left(GetAllEmailFailure(NotFoundSessionException())))); consumeState(Stream.value(Left(GetAllEmailFailure(NotFoundSessionException()))));
} }
} }
EmailFilter _getEmailFilterForLoadMailbox({PresentationEmail? oldestEmail}) {
if (selectedMailboxId?.isNotFavoriteMailboxId != true) {
return EmailFilter(
filter: _getFilterConditionForLoadMailbox(oldestEmail: oldestEmail),
filterOption: mailboxDashBoardController.filterMessageOption.value,
);
} else {
return EmailFilter(
filter: _getFilterConditionForLoadMailbox(oldestEmail: oldestEmail),
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: selectedMailboxId,
);
}
}
Filter _getFilterConditionForLoadMailbox({PresentationEmail? oldestEmail}) {
if (selectedMailboxId?.isNotFavoriteMailboxId != true) {
return EmailFilterCondition(
hasKeyword: KeyWordIdentifier.emailFlagged.value,
before: oldestEmail?.receivedAt,
);
} else {
return getFilterCondition(
mailboxIdSelected: selectedMailboxId,
oldestEmail: oldestEmail,
);
}
}
EmailFilterCondition getFilterCondition({PresentationEmail? oldestEmail, MailboxId? mailboxIdSelected}) { EmailFilterCondition getFilterCondition({PresentationEmail? oldestEmail, MailboxId? mailboxIdSelected}) {
switch(mailboxDashBoardController.filterMessageOption.value) { switch(mailboxDashBoardController.filterMessageOption.value) {
case FilterMessageOption.all: case FilterMessageOption.all:
@@ -741,11 +772,7 @@ class ThreadController extends BaseController with EmailActionController {
_session!, _session!,
_accountId!, _accountId!,
), ),
emailFilter: EmailFilter( emailFilter: _getEmailFilterForLoadMailbox(),
filter: getFilterCondition(mailboxIdSelected: selectedMailboxId),
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: selectedMailboxId,
),
).last; ).last;
refreshState.fold( refreshState.fold(
@@ -788,16 +815,17 @@ class ThreadController extends BaseController with EmailActionController {
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(), sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
filterOption: mailboxDashBoardController.filterMessageOption.value, filterOption: mailboxDashBoardController.filterMessageOption.value,
filter: getFilterCondition(oldestEmail: oldestEmail, mailboxIdSelected: selectedMailboxId), filter: _getFilterConditionForLoadMailbox(oldestEmail: oldestEmail),
properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
lastEmailId: oldestEmail?.id lastEmailId: oldestEmail?.id,
useCache: selectedMailboxId?.isNotFavoriteMailboxId == true,
) )
)); ));
} }
} }
bool _validatePresentationEmail(PresentationEmail email) { bool _validatePresentationEmail(PresentationEmail email) {
return _belongToCurrentMailboxId(email) return (_belongToCurrentMailboxId(email) || selectedMailboxId?.isNotFavoriteMailboxId != true)
&& _notDuplicatedInCurrentList(email); && _notDuplicatedInCurrentList(email);
} }
@@ -3,6 +3,7 @@ import 'package:jmap_dart_client/jmap/core/reference_id.dart';
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/extensions/keyword_identifier_extension.dart'; import 'package:model/extensions/keyword_identifier_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
extension MailboxIdExtension on MailboxId { extension MailboxIdExtension on MailboxId {
String generatePath() { String generatePath() {
@@ -31,4 +32,7 @@ extension MailboxIdExtension on MailboxId {
} }
String get asString => id.value; String get asString => id.value;
bool get isNotFavoriteMailboxId =>
this != PresentationMailbox.favoriteFolder.id;
} }