TF-3334 Use queue to handle multiple refresh changes mailbox from incoming websocket

This commit is contained in:
dab246
2024-12-23 12:24:50 +07:00
committed by Dat H. Pham
parent 6063f4b832
commit dfd622d7ec
19 changed files with 707 additions and 154 deletions
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:core/presentation/extensions/either_view_state_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
@@ -32,6 +33,8 @@ import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_ru
import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart';
import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart'
if (dart.library.html) 'package:tmail_ui_user/features/network_connection/presentation/web_network_connection_controller.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_message.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_queue_handler.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rules_filter_creator_arguments.dart';
import 'package:tmail_ui_user/features/search/email/presentation/search_email_bindings.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
@@ -90,12 +93,12 @@ class ThreadController extends BaseController with EmailActionController {
bool canLoadMore = false;
bool canSearchMore = false;
MailboxId? _currentMemoryMailboxId;
jmap.State? _currentEmailState;
final ScrollController listEmailController = ScrollController();
final FocusNode focusNodeKeyBoard = FocusNode();
final latestEmailSelectedOrUnselected = Rxn<PresentationEmail>();
@visibleForTesting
bool isListEmailScrollViewJumping = false;
WebSocketQueueHandler? _webSocketQueueHandler;
StreamSubscription<html.Event>? _resizeBrowserStreamSubscription;
@@ -128,6 +131,7 @@ class ThreadController extends BaseController with EmailActionController {
if (PlatformInfo.isWeb) {
_registerBrowserResizeListener();
}
_initWebSocketQueueHandler();
super.onInit();
}
@@ -140,7 +144,6 @@ class ThreadController extends BaseController with EmailActionController {
@override
void onClose() {
_currentMemoryMailboxId = null;
_currentEmailState = null;
listEmailController.dispose();
focusNodeKeyBoard.dispose();
if (PlatformInfo.isWeb) {
@@ -154,8 +157,6 @@ class ThreadController extends BaseController with EmailActionController {
super.handleSuccessViewState(success);
if (success is GetAllEmailSuccess) {
_getAllEmailSuccess(success);
} else if (success is RefreshChangesAllEmailSuccess) {
_refreshChangesAllEmailSuccess(success);
} else if (success is LoadMoreEmailsSuccess) {
_loadMoreEmailsSuccess(success);
} else if (success is SearchEmailSuccess) {
@@ -231,6 +232,13 @@ class ThreadController extends BaseController with EmailActionController {
});
}
void _initWebSocketQueueHandler() {
_webSocketQueueHandler = WebSocketQueueHandler(
processMessageCallback: _handleWebSocketMessage,
onErrorCallback: onError,
);
}
void _resetLoadingMore() {
if (loadingMoreStatus.value == LoadingMoreStatus.running) {
loadingMoreStatus.value = LoadingMoreStatus.idle;
@@ -383,8 +391,8 @@ class ThreadController extends BaseController with EmailActionController {
log('ThreadController::_getAllEmailSuccess: GetAllForMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}');
return;
}
_currentEmailState = success.currentEmailState;
log('ThreadController::_getAllEmailSuccess():COUNT = ${success.emailList.length} | EMAIL_STATE = $_currentEmailState');
mailboxDashBoardController.setCurrentEmailState(success.currentEmailState);
log('ThreadController::_getAllEmailSuccess():COUNT = ${success.emailList.length} | EMAIL_STATE = ${mailboxDashBoardController.currentEmailState}');
final newListEmail = success.emailList.syncPresentationEmail(
mapMailboxById: mailboxDashBoardController.mapMailboxById,
selectedMailbox: selectedMailbox,
@@ -413,8 +421,7 @@ class ThreadController extends BaseController with EmailActionController {
log('ThreadController::_refreshChangesAllEmailSuccess: RefreshedMailboxId = ${success.currentMailboxId?.asString} | SELECTED_MAILBOX_ID = ${selectedMailboxId?.asString} | SELECTED_MAILBOX_NAME = ${selectedMailbox?.name?.name}');
return;
}
_currentEmailState = success.currentEmailState;
mailboxDashBoardController.setCurrentEmailState(success.currentEmailState);
log('ThreadController::_refreshChangesAllEmailSuccess: COUNT = ${success.emailList.length}');
final emailsBeforeChanges = mailboxDashBoardController.emailsInCurrentMailbox;
final emailsAfterChanges = success.emailList;
@@ -515,31 +522,112 @@ class ThreadController extends BaseController with EmailActionController {
void _refreshEmailChanges({jmap.State? newState}) {
log('ThreadController::_refreshEmailChanges(): newState: $newState');
if (searchController.isSearchEmailRunning) {
_searchEmail(limit: limitEmailFetched, needRefreshSearchState: true);
} else {
if (_currentEmailState == null ||
_currentEmailState == newState ||
_session == null ||
_accountId == null) {
return;
if (_accountId == null ||
_session == null ||
mailboxDashBoardController.currentEmailState == null ||
newState == null) {
return;
}
_webSocketQueueHandler?.enqueue(WebSocketMessage(newState: newState));
}
Future<void> _handleWebSocketMessage(WebSocketMessage message) async {
try {
if (mailboxDashBoardController.currentEmailState == null ||
mailboxDashBoardController.currentEmailState == message.newState) {
log('ThreadController::_handleWebSocketMessage:Skipping redundant state: ${message.newState}');
return Future.value();
}
consumeState(_refreshChangesEmailsInMailboxInteractor.execute(
if (searchController.isSearchEmailRunning) {
await _refreshChangeSearchEmail();
} else {
await _refreshChangeListEmail();
}
} catch (e, stackTrace) {
logError('ThreadController::_handleWebSocketMessage:Error processing state: $e');
onError(e, stackTrace);
} finally {
if (mailboxDashBoardController.currentEmailState != null) {
_webSocketQueueHandler?.removeMessagesUpToCurrent(
mailboxDashBoardController.currentEmailState!.value);
}
}
}
Future<void> _refreshChangeSearchEmail() async {
log('ThreadController::_refreshChangeSearchEmail:');
if (limitEmailFetched.value > ThreadConstants.maximumEmailQueryLimit &&
listEmailController.hasClients) {
listEmailController.jumpTo(0);
}
canSearchMore = false;
searchController.updateFilterEmail(
positionOption: option(
_searchEmailFilter.sortOrderType.isScrollByPosition(),
0,
),
beforeOption: const None(),
);
searchController.activateSimpleSearch();
final searchViewState = await _searchEmailInteractor.execute(
_session!,
_accountId!,
limit: limitEmailFetched,
position: _searchEmailFilter.position,
sort: _searchEmailFilter.sortOrderType.getSortOrder().toNullable(),
filter: _searchEmailFilter.mappingToEmailFilterCondition(
moreFilterCondition: _getFilterCondition(),
),
properties: EmailUtils.getPropertiesForEmailGetMethod(
_session!,
_accountId!,
_currentEmailState!,
sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(
_session!,
_accountId!,
),
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
emailFilter: EmailFilter(
filter: _getFilterCondition(mailboxIdSelected: selectedMailboxId),
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: selectedMailboxId,
),
));
),
needRefreshSearchState: true,
).last;
final searchState = searchViewState
.foldSuccessWithResult<SearchEmailSuccess>();
if (searchState is SearchEmailSuccess) {
_searchEmailsSuccess(searchState);
} else {
mailboxDashBoardController.updateRefreshAllEmailState(
Left(RefreshAllEmailFailure()));
canSearchMore = false;
mailboxDashBoardController.emailsInCurrentMailbox.clear();
onDataFailureViewState(searchState);
}
}
Future<void> _refreshChangeListEmail() async {
log('ThreadController::_refreshChangeListEmail:');
final refreshViewState = await _refreshChangesEmailsInMailboxInteractor.execute(
_session!,
_accountId!,
mailboxDashBoardController.currentEmailState!,
sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(
_session!,
_accountId!,
),
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
emailFilter: EmailFilter(
filter: _getFilterCondition(mailboxIdSelected: selectedMailboxId),
filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: selectedMailboxId,
),
).last;
final refreshState = refreshViewState
.foldSuccessWithResult<RefreshChangesAllEmailSuccess>();
if (refreshState is RefreshChangesAllEmailSuccess) {
_refreshChangesAllEmailSuccess(refreshState);
} else {
onDataFailureViewState(refreshState);
}
}
@@ -1168,12 +1256,10 @@ class ThreadController extends BaseController with EmailActionController {
}
void goToCreateEmailRuleView() async {
final accountId = mailboxDashBoardController.accountId.value;
final session = mailboxDashBoardController.sessionCurrent;
if (accountId != null && session != null) {
if (_accountId != null && _session != null) {
final arguments = RulesFilterCreatorArguments(
accountId,
session,
_accountId!,
_session!,
mailboxDestination: selectedMailbox
);
@@ -1182,7 +1268,7 @@ class ThreadController extends BaseController with EmailActionController {
: await push(AppRoutes.rulesFilterCreator, arguments: arguments);
if (newRuleFilterRequest is CreateNewEmailRuleFilterRequest) {
_createNewRuleFilterAction(accountId, newRuleFilterRequest);
_createNewRuleFilterAction(_accountId!, newRuleFilterRequest);
}
} else {
logError('ThreadController::goToCreateEmailRuleView: Account or Session is NULL');