Skip to content

Commit

Permalink
TF-2830 Add recipient directly to account when click + Add recipients
Browse files Browse the repository at this point in the history
  • Loading branch information
dab246 authored and hoangdat committed May 8, 2024
1 parent 045572a commit bd06d31
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
class NotFoundForwardException implements Exception {
NotFoundForwardException();
}
class NotFoundForwardException implements Exception {}

class RecipientListIsEmptyException implements Exception {}

class RecipientListWithInvalidEmailsException implements Exception {}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ class ForwardView extends GetWidget<ForwardController> with AppLoaderMixin {
onAddContactCallback: (listRecipientsSelected) {
controller.addRecipientAction(context, listRecipientsSelected);
},
onExceptionCallback: (isListEmailEmpty) {
controller.handleExceptionCallback(context, isListEmailEmpty);
onExceptionCallback: (exception) {
controller.handleExceptionCallback(context, exception);
},
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -19,14 +20,15 @@ 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';
import 'package:tmail_ui_user/main/utils/app_utils.dart';

typedef OnSuggestionContactCallbackAction = Future<List<EmailAddress>> Function(String query);
typedef OnAddListContactCallbackAction = Function(List<EmailAddress> listEmailAddress);
typedef OnExceptionAddListContactCallbackAction = Function(bool isListEmpty);
typedef OnExceptionAddListContactCallbackAction = Function(Exception exception);

class AutocompleteContactTextFieldWithTags extends StatefulWidget {

Expand Down Expand Up @@ -61,7 +63,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta

late List<EmailAddress> listEmailAddress;

Timer? _gapBetweenTagChangedAndFindSuggestion;
bool lastTagFocused = false;

@override
Expand All @@ -70,12 +71,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
listEmailAddress = widget.listEmailAddress;
}

@override
void dispose() {
_gapBetweenTagChangedAndFindSuggestion?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
final itemTagEditor = TagEditor<SuggestionEmailAddress>(
Expand Down Expand Up @@ -118,7 +113,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
),
onSubmitted: (value) => _addEmailAddressToInputFieldAction(
context: context,
emailAddress: EmailAddress(null, value)
emailAddress: EmailAddress(null, value),
isClearInput: true
),
textStyle: const TextStyle(
color: Colors.black,
Expand All @@ -141,16 +137,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
setState(() => 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(
Expand All @@ -162,7 +149,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
selectedContactCallbackAction: (contact) {
_addEmailAddressToInputFieldAction(
context: context,
emailAddress: contact);
emailAddress: contact
);
tagEditorState.closeSuggestionBox();
tagEditorState.resetTextField();
},
Expand Down Expand Up @@ -211,10 +199,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
}

FutureOr<List<SuggestionEmailAddress>> _findSuggestions(String query) async {
if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) {
return [];
}

final processedQuery = query.trim();

if (processedQuery.isEmpty) {
Expand Down Expand Up @@ -260,17 +244,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
.map((emailAddress) => SuggestionEmailAddress(emailAddress, state: SuggestionEmailState.duplicated));
}

void _handleGapBetweenTagChangedAndFindSuggestion() {
log('_AutocompleteContactTextFieldWithTagsState::_handleGapBetweenTagChangedAndFindSuggestion(): Timeout');
}

bool _isValidAllEmailAddress(List<EmailAddress> addedEmailAddress) {
return addedEmailAddress.every((addedMail) => addedMail.emailAddress.isEmail || AppUtils.isEmailLocalhost(addedMail.emailAddress));
}

bool _inputFieldIsEmpty() {
return widget.controller?.text.isEmpty == true;
}
bool _validateListEmailAddressIsValid(List<EmailAddress> listEmailAddress) => listEmailAddress.every(_validateEmailAddressIsValid);

Widget _buildAddRecipientButton(BuildContext context, {double? maxWidth}) {
return MaterialTextIconButton(
Expand All @@ -281,80 +255,167 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
labelColor: Colors.white,
iconColor: Colors.white,
minimumSize: Size(maxWidth ?? 167, PlatformInfo.isMobile ? 44 : 54),
onTap: _handleAddRecipientAction
onTap: () => _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<EmailAddress>.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<EmailAddress>.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,
Expand Down

0 comments on commit bd06d31

Please sign in to comment.