Skip to content

Commit

Permalink
TF-3334 Use queue to handle multiple refresh changes mailbox from inc…
Browse files Browse the repository at this point in the history
…oming websocket
  • Loading branch information
dab246 committed Dec 23, 2024
1 parent bd94d2d commit e321654
Show file tree
Hide file tree
Showing 19 changed files with 696 additions and 143 deletions.
1 change: 1 addition & 0 deletions core/lib/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export 'presentation/extensions/string_extension.dart';
export 'presentation/extensions/tap_down_details_extension.dart';
export 'domain/extensions/media_type_extension.dart';
export 'presentation/extensions/map_extensions.dart';
export 'presentation/extensions/either_view_state_extension.dart';

// Exceptions
export 'domain/exceptions/download_file_exception.dart';
Expand Down
12 changes: 12 additions & 0 deletions core/lib/presentation/extensions/either_view_state_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';

extension EitherViewStateExtension on Either<Failure, Success> {
dynamic foldSuccessWithResult<T>() {
return fold(
(failure) => failure,
(success) => success is T ? success as T : null,
);
}
}
28 changes: 14 additions & 14 deletions lib/features/base/base_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,7 @@ abstract class BaseController extends GetxController

void onData(Either<Failure, Success> newState) {
viewState.value = newState;
viewState.value.fold(
(failure) {
if (failure is FeatureFailure) {
final isUrgentException = validateUrgentException(failure.exception);
if (isUrgentException) {
handleUrgentException(failure: failure, exception: failure.exception);
} else {
handleFailureViewState(failure);
}
} else {
handleFailureViewState(failure);
}
},
handleSuccessViewState);
viewState.value.fold(onDataFailureViewState, handleSuccessViewState);
}

void onError(dynamic error, StackTrace stackTrace) {
Expand Down Expand Up @@ -272,6 +259,19 @@ abstract class BaseController extends GetxController
}
}

void onDataFailureViewState(Failure failure) {
if (failure is FeatureFailure) {
final isUrgentException = validateUrgentException(failure.exception);
if (isUrgentException) {
handleUrgentException(failure: failure, exception: failure.exception);
} else {
handleFailureViewState(failure);
}
} else {
handleFailureViewState(failure);
}
}

void handleFailureViewState(Failure failure) async {
logError('$runtimeType::handleFailureViewState():Failure = $failure');
if (failure is LogoutOidcFailure) {
Expand Down
3 changes: 1 addition & 2 deletions lib/features/base/base_mailbox_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ abstract class BaseMailboxController extends BaseController {
teamMailboxesTree.value = tupleTree.value3;
}

Future<void> syncAllMailboxWithDisplayName(BuildContext context) async {
log("BaseMailboxController::syncAllMailboxWithDisplayName");
void syncAllMailboxWithDisplayName(BuildContext context) {
final syncedMailbox = allMailboxes
.map((mailbox) => mailbox.withDisplayName(mailbox.getDisplayName(context)))
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ class DestinationPickerController extends BaseMailboxController {
await buildTree(success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes);
}
if (currentContext != null) {
await syncAllMailboxWithDisplayName(currentContext!);
syncAllMailboxWithDisplayName(currentContext!);
}
} else if (success is RefreshChangesAllMailboxSuccess) {
await refreshTree(success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes);
if (currentContext != null) {
await syncAllMailboxWithDisplayName(currentContext!);
syncAllMailboxWithDisplayName(currentContext!);
}
} else if (success is SearchMailboxSuccess) {
_searchMailboxSuccess(success);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1711,57 +1711,54 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
void _rejectCalendarEventAction(EmailId emailId) {
if (_rejectCalendarEventInteractor == null
|| _displayingEventBlobId == null
|| mailboxDashBoardController.accountId.value == null
|| mailboxDashBoardController.sessionCurrent == null
|| mailboxDashBoardController.sessionCurrent
!.validateCalendarEventCapability(mailboxDashBoardController.accountId.value!)
.isAvailable == false
|| accountId == null
|| session == null
|| session!.validateCalendarEventCapability(accountId!).isAvailable == false
) {
consumeState(Stream.value(Left(CalendarEventRejectFailure())));
} else {
consumeState(_rejectCalendarEventInteractor!.execute(
mailboxDashBoardController.accountId.value!,
accountId!,
{_displayingEventBlobId!},
emailId,
mailboxDashBoardController.sessionCurrent!.getLanguageForCalendarEvent(
session!.getLanguageForCalendarEvent(
LocalizationService.getLocaleFromLanguage(),
mailboxDashBoardController.accountId.value!)));
accountId!,
),
));
}
}

void _maybeCalendarEventAction(EmailId emailId) {
if (_maybeCalendarEventInteractor == null
|| _displayingEventBlobId == null
|| mailboxDashBoardController.accountId.value == null
|| mailboxDashBoardController.sessionCurrent == null
|| mailboxDashBoardController.sessionCurrent
!.validateCalendarEventCapability(mailboxDashBoardController.accountId.value!)
.isAvailable == false
|| accountId == null
|| session == null
|| session!.validateCalendarEventCapability(accountId!).isAvailable == false
) {
consumeState(Stream.value(Left(CalendarEventMaybeFailure())));
} else {
consumeState(_maybeCalendarEventInteractor!.execute(
mailboxDashBoardController.accountId.value!,
accountId!,
{_displayingEventBlobId!},
emailId,
mailboxDashBoardController.sessionCurrent!.getLanguageForCalendarEvent(
session!.getLanguageForCalendarEvent(
LocalizationService.getLocaleFromLanguage(),
mailboxDashBoardController.accountId.value!)));
accountId!,
),
));
}
}

void calendarEventSuccess(CalendarEventReplySuccess success) {
final session = mailboxDashBoardController.sessionCurrent;
final accountId = mailboxDashBoardController.accountId.value;

if (session == null || accountId == null) {
consumeState(Stream.value(Left(StoreEventAttendanceStatusFailure(exception: NotFoundSessionException()))));
return;
}

consumeState(_storeEventAttendanceStatusInteractor.execute(
session,
accountId,
session!,
accountId!,
success.emailId,
success.getEventActionType()
));
Expand Down Expand Up @@ -1839,24 +1836,23 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}

Future<void> previewPDFFileAction(BuildContext context, Attachment attachment) async {
final accountId = mailboxDashBoardController.accountId.value;
final downloadUrl = mailboxDashBoardController.sessionCurrent
?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl);

if (accountId == null || downloadUrl == null) {
if (accountId == null || session == null) {
appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable);
return;
}
final downloadUrl = session!.getDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);

await Get.generalDialog(
barrierColor: Colors.black.withOpacity(0.8),
pageBuilder: (_, __, ___) {
return PointerInterceptor(
child: PDFViewer(
attachment: attachment,
accountId: accountId,
accountId: accountId!,
downloadUrl: downloadUrl,
downloadAction: _downloadPDFFile,
printAction: _printPDFFile,
Expand All @@ -1876,7 +1872,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}

final listEmailAddressMailTo = listEmailAddressAttendees
.where((emailAddress) => emailAddress.emailAddress.isNotEmpty && emailAddress.emailAddress != mailboxDashBoardController.sessionCurrent?.username.value)
.where((emailAddress) {
return emailAddress.emailAddress.isNotEmpty &&
emailAddress.emailAddress != session?.username.value;
})
.toSet()
.toList();

Expand Down
100 changes: 69 additions & 31 deletions lib/features/mailbox/presentation/mailbox_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_ma
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.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/search/mailbox/presentation/search_mailbox_bindings.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
Expand Down Expand Up @@ -94,6 +96,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM

MailboxId? _newFolderId;
NavigationRouter? _navigationRouter;
WebSocketQueueHandler? _webSocketQueueHandler;

final _openMailboxEventController = StreamController<OpenMailboxViewEvent>();
final mailboxListScrollController = ScrollController();
Expand Down Expand Up @@ -128,6 +131,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
@override
void onInit() {
_registerObxStreamListener();
_initWebSocketQueueHandler();
super.onInit();
}

Expand All @@ -145,6 +149,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
void onClose() {
_openMailboxEventController.close();
mailboxListScrollController.dispose();
_webSocketQueueHandler?.dispose();
super.onClose();
}

Expand All @@ -153,8 +158,6 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
super.handleSuccessViewState(success);
if (success is GetAllMailboxSuccess) {
_handleGetAllMailboxSuccess(success);
} else if (success is RefreshChangesAllMailboxSuccess) {
_handleRefreshChangesAllMailboxSuccess(success);
} else if (success is CreateNewMailboxSuccess) {
_createNewMailboxSuccess(success);
} else if (success is DeleteMultipleMailboxAllSuccess) {
Expand All @@ -181,8 +184,6 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
_renameMailboxFailure(failure);
} else if (failure is DeleteMultipleMailboxFailure) {
_deleteMailboxFailure(failure);
} else if (failure is RefreshChangesAllMailboxFailure) {
_clearNewFolderId();
}
}

Expand All @@ -204,13 +205,6 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
if (PlatformInfo.isIOS) {
_updateMailboxIdsBlockNotificationToKeychain(success.mailboxList);
}
} else if (success is RefreshChangesAllMailboxSuccess) {
_selectSelectedMailboxDefault();
mailboxDashBoardController.refreshSpamReportBanner();

if (_newFolderId != null) {
_redirectToNewFolder();
}
}
});
}
Expand Down Expand Up @@ -258,6 +252,13 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
});
}

void _initWebSocketQueueHandler() {
_webSocketQueueHandler = WebSocketQueueHandler(
processMessageCallback: _handleWebSocketMessage,
onErrorCallback: onError,
);
}

void _initCollapseMailboxCategories() {
if (kIsWeb && currentContext != null
&& (responsiveUtils.isMobile(currentContext!) || responsiveUtils.isTablet(currentContext!))) {
Expand All @@ -283,17 +284,66 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
if (accountId == null ||
session == null ||
currentMailboxState == null ||
newState == currentMailboxState) {
newState == null) {
_newFolderId = null;
return;
}

refreshMailboxChanges(
session!,
accountId!,
currentMailboxState!,
properties: MailboxConstants.propertiesDefault,
);
_webSocketQueueHandler?.enqueue(WebSocketMessage(newState: newState));
}

Future<void> _handleWebSocketMessage(WebSocketMessage message) async {
try {
if (currentMailboxState == message.newState) {
log('MailboxController::_handleWebSocketMessage:Skipping redundant state: ${message.newState}');
return Future.value();
}

final refreshViewState = await refreshAllMailboxInteractor!.execute(
session!,
accountId!,
currentMailboxState!,
properties: MailboxConstants.propertiesDefault,
).last;

final refreshState = refreshViewState
.foldSuccessWithResult<RefreshChangesAllMailboxSuccess>();

if (refreshState is RefreshChangesAllMailboxSuccess) {
await _handleRefreshChangeMailboxSuccess(refreshState);
} else {
_clearNewFolderId();
onDataFailureViewState(refreshState);
}
} catch (e, stackTrace) {
logError('MailboxController::_processMailboxStateQueue:Error processing state: $e');
onError(e, stackTrace);
}
if (currentMailboxState != null) {
_webSocketQueueHandler?.removeMessagesUpToCurrent(currentMailboxState!.value);
}
}

Future<void> _handleRefreshChangeMailboxSuccess(RefreshChangesAllMailboxSuccess success) async {
currentMailboxState = success.currentMailboxState;
log('MailboxController::_handleRefreshChangeMailboxSuccess:currentMailboxState: $currentMailboxState');
final listMailboxDisplayed = success
.mailboxList
.listSubscribedMailboxesAndDefaultMailboxes;

await refreshTree(listMailboxDisplayed);

if (currentContext != null) {
syncAllMailboxWithDisplayName(currentContext!);
}
_setMapMailbox();
_setOutboxMailbox();
_selectSelectedMailboxDefault();
mailboxDashBoardController.refreshSpamReportBanner();

if (_newFolderId != null) {
_redirectToNewFolder();
}
}

void _setMapMailbox() {
Expand Down Expand Up @@ -1078,7 +1128,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
final listMailboxDisplayed = success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes;
await buildTree(listMailboxDisplayed);
if (currentContext != null) {
await syncAllMailboxWithDisplayName(currentContext!);
syncAllMailboxWithDisplayName(currentContext!);
}
_setMapMailbox();
_setOutboxMailbox();
Expand All @@ -1105,18 +1155,6 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
mailboxIds: mailboxIdsBlockNotification);
}

void _handleRefreshChangesAllMailboxSuccess(RefreshChangesAllMailboxSuccess success) async {
currentMailboxState = success.currentMailboxState;
log('MailboxController::_handleRefreshChangesAllMailboxSuccess:currentMailboxState: $currentMailboxState');
final listMailboxDisplayed = success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes;
await refreshTree(listMailboxDisplayed);
if (currentContext != null) {
await syncAllMailboxWithDisplayName(currentContext!);
}
_setMapMailbox();
_setOutboxMailbox();
}

void _unsubscribeMailboxAction(MailboxId mailboxId) {
if (session != null && accountId != null) {
final subscribeRequest = generateSubscribeRequest(
Expand Down
Loading

0 comments on commit e321654

Please sign in to comment.