import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/model.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:super_tag_editor/tag_editor.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/mixin/date_range_picker_mixin.dart'; import 'package:tmail_ui_user/features/base/model/filter_filter.dart'; import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart'; import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/get_device_contact_suggestions_state.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_all_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_device_contact_suggestions_interactor.dart'; import 'package:tmail_ui_user/features/email_recovery/presentation/controller/input_field_focus_manager.dart'; import 'package:tmail_ui_user/features/email_recovery/presentation/extension/handle_auto_create_email_address_tag_extension.dart'; import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_arguments.dart'; import 'package:tmail_ui_user/features/email_recovery/presentation/model/session_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; class EmailRecoveryController extends BaseController with DateRangePickerMixin { GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor; GetAutoCompleteInteractor? _getAutoCompleteInteractor; GetDeviceContactSuggestionsInteractor? _getDeviceContactSuggestionsInteractor; final deletionDateFieldSelected = EmailReceiveTimeType.last7Days.obs; final receptionDateFieldSelected = EmailReceiveTimeType.allTime.obs; final startDeletionDate = Rxn(); final endDeletionDate = Rxn(); final startReceptionDate = Rxn(); final endReceptionDate = Rxn(); final hasAttachment = false.obs; final recipientsExpandMode = ExpandMode.EXPAND.obs; final senderExpandMode = ExpandMode.EXPAND.obs; final focusManager = InputFieldFocusManager.initial(); List listRecipients = []; List listSenders = []; List deletionDateTimeTypes = []; List receptionDateTimeTypes = []; TextEditingController subjectFieldInputController = TextEditingController(); TextEditingController recipientsFieldInputController = TextEditingController(); TextEditingController senderFieldInputController = TextEditingController(); ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact; final GlobalKey recipientsFieldKey = GlobalKey(); final GlobalKey senderFieldKey = GlobalKey(); EmailRecoveryArguments? arguments; AccountId? _accountId; Session? _session; int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete; @override void onInit() { _registerFocusListener(); log('EmailRecoveryController::onInit():arguments: ${Get.arguments}'); arguments = Get.arguments; super.onInit(); } @override void onReady() { log('EmailRecoveryController::onReady():'); if (arguments != null) { _accountId = arguments!.accountId; _session = arguments!.session; _setUpTimeTypesForFields(arguments!.session); _setUpMinInputLengthAutocomplete(); } if (PlatformInfo.isMobile) { Future.delayed( const Duration(milliseconds: 500), () => _checkContactPermission(), ); } injectAutoCompleteBindings(_session, _accountId); super.onReady(); } void _setUpTimeTypesForFields(Session session) { deletionDateTimeTypes = FilterField .deletionDate .getSupportedTimeTypes(session.getRestorationHorizonAsDateTime()); receptionDateTimeTypes = FilterField .receptionDate .getSupportedTimeTypes(session.getRestorationHorizonAsDateTime()); deletionDateFieldSelected.value = deletionDateTimeTypes.firstOrNull ?? EmailReceiveTimeType.last7Days; deletionDateFieldSelected.value = deletionDateTimeTypes.firstOrNull ?? EmailReceiveTimeType.allTime; } void _setUpMinInputLengthAutocomplete() { if (_session == null || _accountId == null) { minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete; } minInputLengthAutocomplete = getMinInputLengthAutocomplete( session: _session!, accountId: _accountId!, ); } void _checkContactPermission() async { final permissionStatus = await Permission.contacts.status; if (permissionStatus.isGranted) { _contactSuggestionSource = ContactSuggestionSource.all; } else if (!permissionStatus.isPermanentlyDenied) { final requestedPermission = await Permission.contacts.request(); _contactSuggestionSource = requestedPermission == PermissionStatus.granted ? ContactSuggestionSource.all : _contactSuggestionSource; } } void _registerFocusListener() { focusManager.subjectFieldFocusNode.addListener(_onSubjectFieldFocusChanged); focusManager.recipientsFieldFocusNode.addListener(_onRecipientsFieldFocusChanged); focusManager.senderFieldFocusNode.addListener(_onSenderFieldFocusChanged); } void _onSubjectFieldFocusChanged() { if (focusManager.subjectFieldFocusNode.hasFocus) { senderExpandMode.value = ExpandMode.COLLAPSE; recipientsExpandMode.value = ExpandMode.COLLAPSE; _closeSuggestionBox(); } } void _onRecipientsFieldFocusChanged() { if (focusManager.recipientsFieldFocusNode.hasFocus) { recipientsExpandMode.value = ExpandMode.EXPAND; senderExpandMode.value = ExpandMode.COLLAPSE; _closeSuggestionBox(); } else { recipientsExpandMode.value = ExpandMode.COLLAPSE; autoCreateTagForFilterField(FilterField.recipients); } } void _onSenderFieldFocusChanged() { if (focusManager.senderFieldFocusNode.hasFocus) { senderExpandMode.value = ExpandMode.EXPAND; recipientsExpandMode.value = ExpandMode.COLLAPSE; _closeSuggestionBox(); } else { senderExpandMode.value = ExpandMode.COLLAPSE; autoCreateTagForFilterField(FilterField.sender); } } void _closeSuggestionBox() { if (recipientsFieldInputController.text.isNotEmpty) { recipientsFieldKey.currentState?.closeSuggestionBox(); } if (senderFieldInputController.text.isNotEmpty) { senderFieldKey.currentState?.closeSuggestionBox(); } } void onSelectDeletionDateRange(BuildContext context) { showMultipleViewDateRangePicker( context, startDeletionDate.value, endDeletionDate.value, onCallbackAction: (startDate, endDate) { _updateDateRangeTime( FilterField.deletionDate, EmailReceiveTimeType.customRange, startDate: startDate, endDate: endDate, ); } ); } void onSelectReceptionDateRange(BuildContext context) { showMultipleViewDateRangePicker( context, startReceptionDate.value, endReceptionDate.value, onCallbackAction: (startDate, endDate) { _updateDateRangeTime( FilterField.receptionDate, EmailReceiveTimeType.customRange, startDate: startDate, endDate: endDate, ); } ); } void _updateDateRangeTime( FilterField field, EmailReceiveTimeType recoveryTimeType, { DateTime? startDate, DateTime? endDate, } ) { if (field == FilterField.deletionDate) { deletionDateFieldSelected.value = recoveryTimeType; startDeletionDate.value = startDate; endDeletionDate.value = endDate; } else { receptionDateFieldSelected.value = recoveryTimeType; startReceptionDate.value = startDate; endReceptionDate.value = endDate; } } void onDeletionDateTypeSelected(BuildContext context, EmailReceiveTimeType type) { if (type == EmailReceiveTimeType.customRange) { onSelectDeletionDateRange(context); } else { _updateDateRangeTime( FilterField.deletionDate, type, startDate: type.toLatestUTCDate()?.value, endDate: DateTime.now(), ); } } void onReceptionDateTypeSelected(BuildContext context, EmailReceiveTimeType type) { if (type == EmailReceiveTimeType.customRange) { onSelectReceptionDateRange(context); } else { _updateDateRangeTime( FilterField.receptionDate, type, ); } } void onChangeHasAttachment(bool? value) { hasAttachment.value = value ?? false; } Future> getAutoCompleteSuggestion(String word, {int? limit}) async { log('EmailRecoveryController::getAutoCompleteSuggestion(): $word | $_contactSuggestionSource'); _getAllAutoCompleteInteractor = getBinding(); _getAutoCompleteInteractor = getBinding(); _getDeviceContactSuggestionsInteractor = getBinding(); final autoCompletePattern = AutoCompletePattern( word: word, limit: limit, accountId: _accountId ); if (_contactSuggestionSource == ContactSuggestionSource.all) { if (_getAllAutoCompleteInteractor != null) { return await _getAllAutoCompleteInteractor! .execute(autoCompletePattern) .then((value) => value.fold( (failure) => [], (success) => success is GetAutoCompleteSuccess ? success.listEmailAddress : [] )); } else if (_getDeviceContactSuggestionsInteractor != null) { return await _getDeviceContactSuggestionsInteractor! .execute(autoCompletePattern) .then((value) => value.fold( (failure) => [], (success) => success is GetDeviceContactSuggestionsSuccess ? success.listEmailAddress : [] )); } else { return []; } } else { if (_getAutoCompleteInteractor == null) { return []; } else { return await _getAutoCompleteInteractor! .execute(autoCompletePattern) .then((value) => value.fold( (failure) => [], (success) => success is GetAutoCompleteSuccess ? success.listEmailAddress : [] )); } } } void showFullEmailAddress(FilterField field) { switch (field) { case FilterField.recipients: recipientsExpandMode.value = ExpandMode.EXPAND; break; case FilterField.sender: senderExpandMode.value = ExpandMode.EXPAND; break; default: break; } } void updateListEmailAddress( FilterField field, List listEmailAddress, ) { switch (field) { case FilterField.recipients: listRecipients = List.from(listEmailAddress); log('EmailRecoveryController::updateListEmailAddress(): listRecipients: ${listRecipients.first}'); break; case FilterField.sender: listSenders = List.from(listEmailAddress); log('EmailRecoveryController::updateListEmailAddress(): listSenders: ${listSenders.first}'); break; default: break; } } void onRestore(BuildContext context) { KeyboardUtils.hideKeyboard(context); UTCDate? deletedBefore; UTCDate? deletedAfter; UTCDate? receivedBefore; UTCDate? receivedAfter; if (deletionDateFieldSelected.value == EmailReceiveTimeType.customRange) { deletedBefore = endDeletionDate.value?.toUTCDate(); deletedAfter = startDeletionDate.value?.toUTCDate(); receivedBefore = endReceptionDate.value?.toUTCDate(); receivedAfter = startReceptionDate.value?.toUTCDate(); } else { deletedBefore = DateTime.now().toUTCDate(); deletedAfter = deletionDateFieldSelected.value.toOldestUTCDate(); receivedBefore = DateTime.now().toUTCDate(); receivedAfter = receptionDateFieldSelected.value.toOldestUTCDate(); } final emailRecoveryAction = EmailRecoveryAction( deletedBefore: deletedBefore, deletedAfter: deletedAfter, receivedBefore: receivedBefore, receivedAfter: receivedAfter, hasAttachment: hasAttachment.value, subject: subjectFieldInputController.text, recipients: listRecipients.isNotEmpty ? listRecipients.map((emailAddress) => emailAddress.emailAddress).toList() : null, sender: listSenders.isNotEmpty ? listSenders.first.emailAddress : null ); log('EmailRecoveryController::onRestore(): emailRecoveryAction: $emailRecoveryAction'); popBack(result: emailRecoveryAction); } void closeView(BuildContext context) { KeyboardUtils.hideKeyboard(context); popBack(); } String getRestorationHorizonAsString() => arguments!.session.getRestorationHorizonAsString(); DateTime getRestorationHorizonAsDateTime() => arguments!.session.getRestorationHorizonAsDateTime(); @override void onClose() { focusManager.subjectFieldFocusNode.removeListener(_onSubjectFieldFocusChanged); focusManager.recipientsFieldFocusNode.removeListener(_onRecipientsFieldFocusChanged); focusManager.senderFieldFocusNode.removeListener(_onSenderFieldFocusChanged); subjectFieldInputController.dispose(); recipientsFieldInputController.dispose(); senderFieldInputController.dispose(); focusManager.dispose(); listSenders.clear(); listRecipients.clear(); deletionDateTimeTypes.clear(); receptionDateTimeTypes.clear(); super.onClose(); } }