import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.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/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:model/autocomplete/auto_complete_pattern.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:debounce_throttle/debounce_throttle.dart'; import 'package:tmail_ui_user/features/base/base_controller.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/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_with_device_contact_interactor.dart'; import 'package:tmail_ui_user/features/contact/presentation/model/contact_arguments.dart'; import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_suggestion_box_item.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class ContactController extends BaseController { final TextEditingController textInputSearchController = TextEditingController(); final FocusNode textInputSearchFocus = FocusNode(); ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact; final searchQuery = SearchQuery.initial().obs; final listContactSearched = RxList(); GetAutoCompleteWithDeviceContactInteractor? _getAutoCompleteWithDeviceContactInteractor; GetAutoCompleteInteractor? _getAutoCompleteInteractor; final Debouncer _deBouncerTime = Debouncer(const Duration(milliseconds: 500), initialValue: ''); AccountId? _accountId; Session? _session; ContactArguments? arguments; EmailAddress? contactSelected; SelectedContactCallbackAction? onSelectedContactCallback; VoidCallback? onDismissContactView; @override void onInit() { super.onInit(); log('ContactController::onInit():arguments: ${Get.arguments}'); arguments = Get.arguments; _deBouncerTime.values.listen((value) { searchQuery.value = SearchQuery(value); _searchContactByNameOrEmail(searchQuery.value.value); }); } @override void onReady() async { super.onReady(); log('ContactController::onReady():'); textInputSearchFocus.requestFocus(); if (arguments != null) { _accountId = arguments!.accountId; _session = arguments!.session; final listContactSelected = arguments!.listContactSelected; log('ContactController::onReady(): arguments: $arguments'); log('ContactController::onReady(): listContactSelected: $listContactSelected'); if (listContactSelected.isNotEmpty) { contactSelected = EmailAddress(listContactSelected.first, listContactSelected.first); } injectAutoCompleteBindings(_session, _accountId); } if (PlatformInfo.isMobile) { Future.delayed( const Duration(milliseconds: 500), () => _checkContactPermission()); } } @override void onClose() { log('ContactController::onClose():'); textInputSearchFocus.dispose(); textInputSearchController.dispose(); _deBouncerTime.cancel(); super.onClose(); } void onTextSearchChange(String text) { _deBouncerTime.value = text; } void onSearchTextAction(String text) { _deBouncerTime.value = text; } void clearAllTextInputSearchForm() { textInputSearchController.clear(); searchQuery.value = SearchQuery.initial(); textInputSearchFocus.requestFocus(); } 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 _searchContactByNameOrEmail(String query) async { log('ContactController::_searchContactByNameOrEmail(): query: $query'); final listContact = await _getAutoCompleteSuggestion(query); listContactSearched.value = listContact; } @override void injectAutoCompleteBindings(Session? session, AccountId? accountId) { try { super.injectAutoCompleteBindings(session, accountId); _getAutoCompleteWithDeviceContactInteractor = Get.find(); _getAutoCompleteInteractor = Get.find(); } catch (e) { logError('ContactController::injectAutoCompleteBindings(): $e'); } } Future> _getAutoCompleteSuggestion(String query) async { try { _getAutoCompleteWithDeviceContactInteractor = Get.find(); _getAutoCompleteInteractor = Get.find(); } catch (e) { logError('ContactController::getAutoCompleteSuggestion(): Exception $e'); } if (_contactSuggestionSource == ContactSuggestionSource.all) { if (_getAutoCompleteWithDeviceContactInteractor == null || _getAutoCompleteInteractor == null) { return []; } final listEmailAddress = await _getAutoCompleteWithDeviceContactInteractor! .execute(AutoCompletePattern(word: query, limit: 30, accountId: _accountId)) .then((value) => value.fold( (failure) => [], (success) => success is GetAutoCompleteSuccess ? success.listEmailAddress : [])); return listEmailAddress; } else { if (_getAutoCompleteInteractor == null) { return []; } final listEmailAddress = await _getAutoCompleteInteractor! .execute(AutoCompletePattern(word: query, limit: 30, accountId: _accountId)) .then((value) => value.fold( (failure) => [], (success) => success is GetAutoCompleteSuccess ? success.listEmailAddress : [])); return listEmailAddress; } } void selectContact(BuildContext context, EmailAddress emailAddress) { KeyboardUtils.hideKeyboard(context); popBack(result: emailAddress); } void closeContactView(BuildContext context) { clearAllTextInputSearchForm(); KeyboardUtils.hideKeyboard(context); popBack(); } }