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 18, 2024
1 parent 9604036 commit 560b826
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 94 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 @@ -1697,57 +1697,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 @@ -1825,24 +1822,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 @@ -1862,7 +1858,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
112 changes: 81 additions & 31 deletions lib/features/mailbox/presentation/mailbox_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_catego
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/open_mailbox_view_event.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_state_manager.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart';
Expand Down Expand Up @@ -97,6 +98,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM

final _openMailboxEventController = StreamController<OpenMailboxViewEvent>();
final mailboxListScrollController = ScrollController();
final _mailboxStateManager = MailboxStateManager();

PresentationMailbox? get selectedMailbox => mailboxDashBoardController.selectedMailbox.value;

Expand Down Expand Up @@ -145,6 +147,7 @@ class MailboxController extends BaseMailboxController with MailboxActionHandlerM
void onClose() {
_openMailboxEventController.close();
mailboxListScrollController.dispose();
_mailboxStateManager.dispose();
super.onClose();
}

Expand All @@ -153,8 +156,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 +182,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 +203,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 @@ -283,17 +275,87 @@ 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,
);
_mailboxStateManager.addState(newState);

if (!_mailboxStateManager.isProcessing) {
_processMailboxStateQueue(
session!,
accountId!,
);
}
}

Future<void> _processMailboxStateQueue(
Session session,
AccountId accountId,
) async {
if (_mailboxStateManager.isProcessing) return;

_mailboxStateManager.startProcessing();

while (_mailboxStateManager.isQueueNotEmpty()) {
final nextState = _mailboxStateManager.getFirstState();

if (nextState == currentMailboxState) {
log('MailboxController::_processMailboxStateQueue:Skipping redundant state: $nextState');
continue;
}

log('MailboxController::_processMailboxStateQueue:Processing new state: $nextState & current state = $currentMailboxState');
try {
final refreshViewState = await refreshAllMailboxInteractor!.execute(
session,
accountId,
currentMailboxState!,
properties: MailboxConstants.propertiesDefault,
).last;

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

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

_mailboxStateManager.stopProcessing();
}

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 +1140,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 +1167,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 560b826

Please sign in to comment.