From bd06d3174fa7e6cc7032701763ca701d0d26e150 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 7 May 2024 12:36:12 +0700 Subject: [PATCH] TF-2830 Add recipient directly to account when click `+ Add recipients` --- .../domain/exceptions/forward_exception.dart | 8 +- .../forward/forward_controller.dart | 7 +- .../presentation/forward/forward_view.dart | 4 +- ...complete_contact_text_field_with_tags.dart | 211 +++++++++++------- 4 files changed, 147 insertions(+), 83 deletions(-) diff --git a/lib/features/manage_account/domain/exceptions/forward_exception.dart b/lib/features/manage_account/domain/exceptions/forward_exception.dart index 87e1a25df4..11d2b7b168 100644 --- a/lib/features/manage_account/domain/exceptions/forward_exception.dart +++ b/lib/features/manage_account/domain/exceptions/forward_exception.dart @@ -1,3 +1,5 @@ -class NotFoundForwardException implements Exception { - NotFoundForwardException(); -} +class NotFoundForwardException implements Exception {} + +class RecipientListIsEmptyException implements Exception {} + +class RecipientListWithInvalidEmailsException implements Exception {} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/forward/forward_controller.dart b/lib/features/manage_account/presentation/forward/forward_controller.dart index 0e783cc88e..2e9c1cdfcc 100644 --- a/lib/features/manage_account/presentation/forward/forward_controller.dart +++ b/lib/features/manage_account/presentation/forward/forward_controller.dart @@ -15,6 +15,7 @@ import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/state/banner_state.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/exceptions/forward_exception.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/add_recipients_in_forwarding_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/delete_recipient_in_forwarding_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/edit_local_copy_in_forwarding_request.dart'; @@ -319,12 +320,12 @@ class ForwardController extends BaseController { ); } - void handleExceptionCallback(BuildContext context, bool isListEmailEmpty) { - if (isListEmailEmpty) { + void handleExceptionCallback(BuildContext context, Exception exception) { + if (exception is RecipientListIsEmptyException) { appToast.showToastErrorMessage( context, AppLocalizations.of(context).emptyListEmailForward); - } else { + } else if (exception is RecipientListWithInvalidEmailsException) { appToast.showToastErrorMessage( context, AppLocalizations.of(context).incorrectEmailFormat); diff --git a/lib/features/manage_account/presentation/forward/forward_view.dart b/lib/features/manage_account/presentation/forward/forward_view.dart index abe75cd46c..2a3882002a 100644 --- a/lib/features/manage_account/presentation/forward/forward_view.dart +++ b/lib/features/manage_account/presentation/forward/forward_view.dart @@ -133,8 +133,8 @@ class ForwardView extends GetWidget with AppLoaderMixin { onAddContactCallback: (listRecipientsSelected) { controller.addRecipientAction(context, listRecipientsSelected); }, - onExceptionCallback: (isListEmailEmpty) { - controller.handleExceptionCallback(context, isListEmailEmpty); + onExceptionCallback: (exception) { + controller.handleExceptionCallback(context, exception); }, ); } diff --git a/lib/features/manage_account/presentation/forward/widgets/autocomplete_contact_text_field_with_tags.dart b/lib/features/manage_account/presentation/forward/widgets/autocomplete_contact_text_field_with_tags.dart index 70bcd59edf..59f5407de1 100644 --- a/lib/features/manage_account/presentation/forward/widgets/autocomplete_contact_text_field_with_tags.dart +++ b/lib/features/manage_account/presentation/forward/widgets/autocomplete_contact_text_field_with_tags.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; @@ -19,6 +20,7 @@ import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_em import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_input_tag_item.dart'; import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_suggestion_box_item.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/exceptions/forward_exception.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; @@ -26,7 +28,7 @@ import 'package:tmail_ui_user/main/utils/app_utils.dart'; typedef OnSuggestionContactCallbackAction = Future> Function(String query); typedef OnAddListContactCallbackAction = Function(List listEmailAddress); -typedef OnExceptionAddListContactCallbackAction = Function(bool isListEmpty); +typedef OnExceptionAddListContactCallbackAction = Function(Exception exception); class AutocompleteContactTextFieldWithTags extends StatefulWidget { @@ -61,7 +63,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State listEmailAddress; - Timer? _gapBetweenTagChangedAndFindSuggestion; bool lastTagFocused = false; @override @@ -70,12 +71,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State( @@ -118,7 +113,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State _addEmailAddressToInputFieldAction( context: context, - emailAddress: EmailAddress(null, value) + emailAddress: EmailAddress(null, value), + isClearInput: true ), textStyle: const TextStyle( color: Colors.black, @@ -141,16 +137,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State listEmailAddress.remove(contact)); } ), - onTagChanged: (value) { - _addEmailAddressToInputFieldAction( - context: context, - emailAddress: EmailAddress(null, value) - ); - _gapBetweenTagChangedAndFindSuggestion = Timer( - const Duration(seconds: 1), - _handleGapBetweenTagChangedAndFindSuggestion - ); - }, + onTagChanged: (_) {}, findSuggestions: _findSuggestions, suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) { return Container( @@ -162,7 +149,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State> _findSuggestions(String query) async { - if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) { - return []; - } - final processedQuery = query.trim(); if (processedQuery.isEmpty) { @@ -260,17 +244,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State SuggestionEmailAddress(emailAddress, state: SuggestionEmailState.duplicated)); } - void _handleGapBetweenTagChangedAndFindSuggestion() { - log('_AutocompleteContactTextFieldWithTagsState::_handleGapBetweenTagChangedAndFindSuggestion(): Timeout'); - } - - bool _isValidAllEmailAddress(List addedEmailAddress) { - return addedEmailAddress.every((addedMail) => addedMail.emailAddress.isEmail || AppUtils.isEmailLocalhost(addedMail.emailAddress)); - } - - bool _inputFieldIsEmpty() { - return widget.controller?.text.isEmpty == true; - } + bool _validateListEmailAddressIsValid(List listEmailAddress) => listEmailAddress.every(_validateEmailAddressIsValid); Widget _buildAddRecipientButton(BuildContext context, {double? maxWidth}) { return MaterialTextIconButton( @@ -281,80 +255,167 @@ class _AutocompleteContactTextFieldWithTagsState extends State _handleAddRecipientAction(context) ); } - void _handleAddRecipientAction() { - _hideKeyboardForMobile(); - if (widget.controller?.text.isNotEmpty == true) { - if (!_isDuplicatedRecipient(widget.controller?.text ?? '')) { - _addEmailAddressToInputFieldAction( - context: context, - emailAddress: EmailAddress(null, widget.controller?.text) - ); + void _handleAddRecipientAction(BuildContext context) { + KeyboardUtils.hideKeyboard(context); + + final inputText = widget.controller?.text ?? ''; + + if (inputText.isNotEmpty) { + final emailAddress = EmailAddress(null, inputText); + + if (!_validateEmailAddressIsValid(emailAddress)) { + widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException()); + _resetInputText(); + return; } + + _validateEmailAddressSameDomain( + context: context, + emailAddress: emailAddress, + confirmAction: () { + final newListEmailAddress = List.from([...listEmailAddress, emailAddress]); + + widget.onAddContactCallback?.call(newListEmailAddress); + + _resetInputText(); + if (listEmailAddress.isNotEmpty) { + setState(listEmailAddress.clear); + } + }, + cancelAction: () { + if (listEmailAddress.isNotEmpty) { + widget.onAddContactCallback?.call(listEmailAddress); + setState(listEmailAddress.clear); + } + _resetInputText(); + }, + sameDomainAction: () { + final newListEmailAddress = List.from([...listEmailAddress, emailAddress]); + + widget.onAddContactCallback?.call(newListEmailAddress); + + _resetInputText(); + if (listEmailAddress.isNotEmpty) { + setState(listEmailAddress.clear); + } + }, + duplicatedRecipientAction: () { + if (listEmailAddress.isNotEmpty) { + widget.onAddContactCallback?.call(listEmailAddress); + setState(listEmailAddress.clear); + } + _resetInputText(); + } + ); + _closeSuggestionBox(); return; } if (listEmailAddress.isEmpty) { - widget.onExceptionCallback?.call(true); + widget.onExceptionCallback?.call(RecipientListIsEmptyException()); return; } - if (_isValidAllEmailAddress(listEmailAddress) && _inputFieldIsEmpty()) { - widget.onAddContactCallback?.call(List.from(listEmailAddress)); - setState(() { - widget.controller?.clear(); - listEmailAddress.clear(); - }); - } else { - widget.onExceptionCallback?.call(false); + if (!_validateListEmailAddressIsValid(listEmailAddress)) { + widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException()); + return; } - } - void _closeSuggestionBox() { - keyToEmailTagEditor.currentState?.resetTextField(); - keyToEmailTagEditor.currentState?.closeSuggestionBox(); + widget.onAddContactCallback?.call(List.from(listEmailAddress)); + + _resetInputText(); + setState(listEmailAddress.clear); } - void _hideKeyboardForMobile() { - if (!_responsiveUtils.isDesktop(context)) { - FocusScope.of(context).unfocus(); - } + bool _validateEmailAddressIsValid(EmailAddress emailAddress) { + return GetUtils.isEmail(emailAddress.emailAddress) + || AppUtils.isEmailLocalhost(emailAddress.emailAddress); } - void _addEmailAddressToInputFieldAction({ + void _validateEmailAddressSameDomain({ required BuildContext context, - required EmailAddress emailAddress + required EmailAddress emailAddress, + required VoidCallback? confirmAction, + required VoidCallback? cancelAction, + required VoidCallback? sameDomainAction, + required VoidCallback? duplicatedRecipientAction, }) { if (_isDuplicatedRecipient(emailAddress.emailAddress)) { + duplicatedRecipientAction?.call(); return; } - final validateSameDomain = EmailUtils.isSameDomain( + bool isSameDomain = EmailUtils.isSameDomain( emailAddress: emailAddress.emailAddress, internalDomain: widget.internalDomain ); - if (!validateSameDomain) { + if (isSameDomain) { + sameDomainAction?.call(); + } else { _showWarningDialogWithExternalDomain( context: context, - confirmAction: () { - keyToEmailTagEditor.currentState?.resetTextField(); - setState(() => listEmailAddress.add(emailAddress)); - }, - cancelAction: () { - keyToEmailTagEditor.currentState?.resetTextField(); - } + confirmAction: confirmAction, + cancelAction: cancelAction ); - } else { - keyToEmailTagEditor.currentState?.resetTextField(); - setState(() => listEmailAddress.add(emailAddress)); } } + void _closeSuggestionBox() { + keyToEmailTagEditor.currentState?.closeSuggestionBox(); + } + + void _resetInputText() { + keyToEmailTagEditor.currentState?.resetTextField(); + } + + void _addEmailAddressToInputFieldAction({ + required BuildContext context, + required EmailAddress emailAddress, + bool isClearInput = false + }) { + log('_AutocompleteContactTextFieldWithTagsState::_addEmailAddressToInputFieldAction:emailAddress = $emailAddress'); + if (!_validateEmailAddressIsValid(emailAddress)) { + widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException()); + if (isClearInput) { + _resetInputText(); + } + return; + } + + _validateEmailAddressSameDomain( + context: context, + emailAddress: emailAddress, + confirmAction: () { + if (isClearInput) { + _resetInputText(); + } + setState(() => listEmailAddress.add(emailAddress)); + }, + cancelAction: () { + if (isClearInput) { + _resetInputText(); + } + }, + sameDomainAction: () { + if (isClearInput) { + _resetInputText(); + } + setState(() => listEmailAddress.add(emailAddress)); + }, + duplicatedRecipientAction: () { + if (isClearInput) { + _resetInputText(); + } + } + ); + } + void _showWarningDialogWithExternalDomain({ required BuildContext context, VoidCallback? confirmAction,