import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/utils/app_logger.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.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/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_screen_type.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_changes_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/search_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; 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_creator/domain/model/verification/duplicate_name_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/name_with_space_only_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/special_character_validator.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class DestinationPickerController extends BaseMailboxController { final SearchMailboxInteractor _searchMailboxInteractor; final CreateNewMailboxInteractor _createNewMailboxInteractor; final mailboxAction = Rxn(); final listMailboxSearched = [].obs; final searchState = SearchState.initial().obs; final searchQuery = SearchQuery.initial().obs; final destinationScreenType = DestinationScreenType.destinationPicker.obs; final mailboxDestination = Rxn(); final newNameMailbox = Rxn(); DestinationPickerArguments? arguments; Session? _session; AccountId? accountId; MailboxId? mailboxIdSelected; List listMailboxNameAsStringExist = []; final FocusNode nameInputFocusNode = FocusNode(); final FocusNode searchFocus = FocusNode(); final TextEditingController nameInputController = TextEditingController(); final TextEditingController searchInputController = TextEditingController(); final destinationListScrollController = ScrollController(); DestinationPickerController( this._searchMailboxInteractor, this._createNewMailboxInteractor, TreeBuilder treeBuilder, VerifyNameInteractor verifyNameInteractor, GetAllMailboxInteractor getAllMailboxInteractor, RefreshAllMailboxInteractor refreshAllMailboxInteractor ) : super( treeBuilder, verifyNameInteractor, getAllMailboxInteractor : getAllMailboxInteractor, refreshAllMailboxInteractor : refreshAllMailboxInteractor ); @override void onInit() { super.onInit(); log('DestinationPickerController::onInit():arguments: ${Get.arguments}'); arguments = Get.arguments; } @override void onReady() { super.onReady(); log('DestinationPickerController::onReady():'); if (arguments != null) { mailboxAction.value = arguments!.mailboxAction; mailboxIdSelected = arguments!.mailboxIdSelected; accountId = arguments!.accountId; _session = arguments!.session; getAllMailboxAction(); } } @override void handleSuccessViewState(Success success) async { super.handleSuccessViewState(success); if (success is GetAllMailboxSuccess) { if ((mailboxAction.value == MailboxActions.move || mailboxAction.value == MailboxActions.moveFolderContent) && mailboxIdSelected != null) { await buildTree( success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes, mailboxIdSelected: mailboxIdSelected); } else { await buildTree(success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes); } if (currentContext != null) { syncAllMailboxWithDisplayName(currentContext!); } } else if (success is RefreshChangesAllMailboxSuccess) { await refreshTree(success.mailboxList.listSubscribedMailboxesAndDefaultMailboxes); if (currentContext != null) { syncAllMailboxWithDisplayName(currentContext!); } } else if (success is SearchMailboxSuccess) { _searchMailboxSuccess(success); } else if (success is CreateNewMailboxSuccess) { _createNewMailboxSuccess(success); } } @override void handleFailureViewState(Failure failure) { super.handleFailureViewState(failure); if (failure is SearchMailboxFailure) { _searchMailboxFailure(failure); } else if (failure is CreateNewMailboxFailure) { _createNewMailboxFailure(failure); } } @override void onClose() { log('DestinationPickerController::onClose():'); nameInputFocusNode.dispose(); nameInputController.dispose(); searchFocus.dispose(); searchInputController.dispose(); destinationListScrollController.dispose(); super.onClose(); } void getAllMailboxAction() { if (accountId != null && _session != null) { getAllMailbox(_session!, accountId!); } } void _refreshMailboxChanges(jmap.State currentMailboxState) { if (accountId != null && _session != null) { refreshMailboxChanges(_session!, accountId!, currentMailboxState); } } bool isSearchActive() => searchState.value.searchStatus == SearchStatus.ACTIVE; void enableSearch() { searchState.value = searchState.value.enableSearchState(); } void setNewNameMailbox(String newName) => newNameMailbox.value = newName; String? getErrorInputNameString(BuildContext context) { final nameMailbox = newNameMailbox.value; if (nameInputFocusNode.hasFocus == false && nameMailbox == null) { return null; } return verifyNameInteractor.execute( nameMailbox, [ EmptyNameValidator(), NameWithSpaceOnlyValidator(), DuplicateNameValidator(listMailboxNameAsStringExist), SpecialCharacterValidator() ] ).fold( (failure) { if (failure is VerifyNameFailure) { return failure.getMessage(context); } else { return null; } }, (success) => null ); } bool isCreateMailboxValidated(BuildContext context) { final nameValidated = getErrorInputNameString(context); if (nameInputFocusNode.hasFocus == false && newNameMailbox.value == null) { return false; } if (nameValidated?.isNotEmpty == true) { return false; } return true; } void _createListMailboxNameAsStringInMailboxLocation() { if (mailboxDestination.value == null || mailboxDestination.value == PresentationMailbox.unifiedMailbox) { final allChildrenAtMailboxLocation = (defaultMailboxTree.value.root.childrenItems ?? []) + (personalMailboxTree.value.root.childrenItems ?? []); if (allChildrenAtMailboxLocation.isNotEmpty) { listMailboxNameAsStringExist = allChildrenAtMailboxLocation .where((mailboxNode) => mailboxNode.nameNotEmpty) .map((mailboxNode) => mailboxNode.mailboxNameAsString) .toList(); } else { listMailboxNameAsStringExist = []; } } else { final mailboxNodeLocation = findMailboxNodeById(mailboxDestination.value!.id); if (mailboxNodeLocation != null && mailboxNodeLocation.childrenItems?.isNotEmpty == true) { final allChildrenAtMailboxLocation = mailboxNodeLocation.childrenItems!; listMailboxNameAsStringExist = allChildrenAtMailboxLocation .where((mailboxNode) => mailboxNode.nameNotEmpty) .map((mailboxNode) => mailboxNode.mailboxNameAsString) .toList(); } else { listMailboxNameAsStringExist = []; } } } void disableSearch(BuildContext context) { listMailboxSearched.clear(); searchState.value = searchState.value.disableSearchState(); searchQuery.value = SearchQuery.initial(); searchInputController.clear(); KeyboardUtils.hideKeyboard(context); } void clearSearchText() { searchQuery.value = SearchQuery.initial(); searchFocus.requestFocus(); listMailboxSearched.clear(); } void searchMailbox(BuildContext context, String value) { searchQuery.value = SearchQuery(value); final searchableMailboxList = mailboxAction.value == MailboxActions.moveEmail || mailboxAction.value == MailboxActions.moveFolderContent ? allMailboxes : allMailboxes.listPersonalMailboxes; final mailboxListWithDisplayName = searchableMailboxList .map((mailbox) => mailbox.withDisplayName(mailbox.getDisplayName(context))) .toList(); _searchMailboxAction(mailboxListWithDisplayName, searchQuery.value); } void _searchMailboxAction(List allMailboxes, SearchQuery searchQuery) { if (searchQuery.value.isNotEmpty) { consumeState(_searchMailboxInteractor.execute(allMailboxes, searchQuery)); } else { listMailboxSearched.clear(); } } void _searchMailboxSuccess(SearchMailboxSuccess success) { final mailboxesSearchedWithPath = findMailboxPath(success.mailboxesSearched); listMailboxSearched.value = mailboxesSearchedWithPath; } void _searchMailboxFailure(SearchMailboxFailure failure) { listMailboxSearched.clear(); } void openCreateNewMailboxView(BuildContext context) async { if (mailboxDestination.value == null) { appToast.showToastErrorMessage( currentOverlayContext!, AppLocalizations.of(context).toastMessageErrorNotSelectedFolderWhenCreateNewMailbox); } else { destinationScreenType.value = DestinationScreenType.createNewMailbox; _createListMailboxNameAsStringInMailboxLocation(); } } void _dispatchCreateNewMailboxFolder( Session session, AccountId accountId, CreateNewMailboxRequest request ) async { consumeState(_createNewMailboxInteractor.execute(session, accountId, request)); } void _createNewMailboxSuccess(CreateNewMailboxSuccess success) { if (currentContext != null) { disableSearch(currentContext!); } if (success.currentMailboxState != null) { _refreshMailboxChanges(success.currentMailboxState!); } else { getAllMailboxAction(); } } void _createNewMailboxFailure(CreateNewMailboxFailure failure) { if (currentOverlayContext != null && currentContext != null) { final exception = failure.exception; var messageError = AppLocalizations.of(currentContext!).createNewFolderFailure; if (exception is ErrorMethodResponse) { messageError = exception.description ?? AppLocalizations.of(currentContext!).createNewFolderFailure; } appToast.showToastErrorMessage(currentOverlayContext!, messageError); } } void selectMailboxAction( PresentationMailbox? presentationMailbox, {MailboxNode? mailboxNode} ) { if (mailboxDestination.value == presentationMailbox) { return; } mailboxDestination.value = presentationMailbox; if (presentationMailbox == null || presentationMailbox.id == PresentationMailbox.unifiedMailbox.id) { unAllSelectedMailboxNode(); } else { if (mailboxNode != null) { unAllSelectedMailboxNode(); selectMailboxNode(mailboxNode); } else { final matchedMailboxNode = findMailboxNodeById(presentationMailbox.id); if (matchedMailboxNode != null) { unAllSelectedMailboxNode(); selectMailboxNode(matchedMailboxNode); } } } } void dispatchSelectMailboxDestination(BuildContext context) { KeyboardUtils.hideKeyboard(context); if (mailboxDestination.value == null) { appToast.showToastErrorMessage( currentOverlayContext!, AppLocalizations.of(context).toastMessageErrorNotSelectedFolderWhenCreateNewMailbox); return; } popBack(result: mailboxDestination.value); } void createNewMailboxAction(BuildContext context) { KeyboardUtils.hideKeyboard(context); final nameMailbox = newNameMailbox.value; if (nameMailbox != null && nameMailbox.isNotEmpty) { final parentId = mailboxDestination.value == PresentationMailbox.unifiedMailbox ? null : mailboxDestination.value?.id; _dispatchCreateNewMailboxFolder( _session!, accountId!, CreateNewMailboxRequest( MailboxName(nameMailbox), parentId: parentId)); } backToDestinationScreen(context); } void backToDestinationScreen(BuildContext context) { nameInputController.clear(); KeyboardUtils.hideKeyboard(context); destinationScreenType.value = DestinationScreenType.destinationPicker; } void closeDestinationPicker(BuildContext context) { KeyboardUtils.hideKeyboard(context); popBack(); } }