TF-2830 Add recipient directly to account when click + Add recipients
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
class NotFoundForwardException implements Exception {
|
class NotFoundForwardException implements Exception {}
|
||||||
NotFoundForwardException();
|
|
||||||
}
|
class RecipientListIsEmptyException implements Exception {}
|
||||||
|
|
||||||
|
class RecipientListWithInvalidEmailsException implements Exception {}
|
||||||
@@ -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/base/state/banner_state.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.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/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/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/delete_recipient_in_forwarding_request.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/model/edit_local_copy_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) {
|
void handleExceptionCallback(BuildContext context, Exception exception) {
|
||||||
if (isListEmailEmpty) {
|
if (exception is RecipientListIsEmptyException) {
|
||||||
appToast.showToastErrorMessage(
|
appToast.showToastErrorMessage(
|
||||||
context,
|
context,
|
||||||
AppLocalizations.of(context).emptyListEmailForward);
|
AppLocalizations.of(context).emptyListEmailForward);
|
||||||
} else {
|
} else if (exception is RecipientListWithInvalidEmailsException) {
|
||||||
appToast.showToastErrorMessage(
|
appToast.showToastErrorMessage(
|
||||||
context,
|
context,
|
||||||
AppLocalizations.of(context).incorrectEmailFormat);
|
AppLocalizations.of(context).incorrectEmailFormat);
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ class ForwardView extends GetWidget<ForwardController> with AppLoaderMixin {
|
|||||||
onAddContactCallback: (listRecipientsSelected) {
|
onAddContactCallback: (listRecipientsSelected) {
|
||||||
controller.addRecipientAction(context, listRecipientsSelected);
|
controller.addRecipientAction(context, listRecipientsSelected);
|
||||||
},
|
},
|
||||||
onExceptionCallback: (isListEmailEmpty) {
|
onExceptionCallback: (exception) {
|
||||||
controller.handleExceptionCallback(context, isListEmailEmpty);
|
controller.handleExceptionCallback(context, exception);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+137
-76
@@ -4,6 +4,7 @@ import 'dart:async';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.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/presentation/utils/responsive_utils.dart';
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/utils/platform_info.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_input_tag_item.dart';
|
||||||
import 'package:tmail_ui_user/features/contact/presentation/widgets/contact_suggestion_box_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/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/features/manage_account/presentation/menu/settings_utils.dart';
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.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_config.dart';
|
||||||
@@ -26,7 +28,7 @@ import 'package:tmail_ui_user/main/utils/app_utils.dart';
|
|||||||
|
|
||||||
typedef OnSuggestionContactCallbackAction = Future<List<EmailAddress>> Function(String query);
|
typedef OnSuggestionContactCallbackAction = Future<List<EmailAddress>> Function(String query);
|
||||||
typedef OnAddListContactCallbackAction = Function(List<EmailAddress> listEmailAddress);
|
typedef OnAddListContactCallbackAction = Function(List<EmailAddress> listEmailAddress);
|
||||||
typedef OnExceptionAddListContactCallbackAction = Function(bool isListEmpty);
|
typedef OnExceptionAddListContactCallbackAction = Function(Exception exception);
|
||||||
|
|
||||||
class AutocompleteContactTextFieldWithTags extends StatefulWidget {
|
class AutocompleteContactTextFieldWithTags extends StatefulWidget {
|
||||||
|
|
||||||
@@ -61,7 +63,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
|
|
||||||
late List<EmailAddress> listEmailAddress;
|
late List<EmailAddress> listEmailAddress;
|
||||||
|
|
||||||
Timer? _gapBetweenTagChangedAndFindSuggestion;
|
|
||||||
bool lastTagFocused = false;
|
bool lastTagFocused = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -70,12 +71,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
listEmailAddress = widget.listEmailAddress;
|
listEmailAddress = widget.listEmailAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_gapBetweenTagChangedAndFindSuggestion?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final itemTagEditor = TagEditor<SuggestionEmailAddress>(
|
final itemTagEditor = TagEditor<SuggestionEmailAddress>(
|
||||||
@@ -118,7 +113,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
),
|
),
|
||||||
onSubmitted: (value) => _addEmailAddressToInputFieldAction(
|
onSubmitted: (value) => _addEmailAddressToInputFieldAction(
|
||||||
context: context,
|
context: context,
|
||||||
emailAddress: EmailAddress(null, value)
|
emailAddress: EmailAddress(null, value),
|
||||||
|
isClearInput: true
|
||||||
),
|
),
|
||||||
textStyle: const TextStyle(
|
textStyle: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
@@ -141,16 +137,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
setState(() => listEmailAddress.remove(contact));
|
setState(() => listEmailAddress.remove(contact));
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
onTagChanged: (value) {
|
onTagChanged: (_) {},
|
||||||
_addEmailAddressToInputFieldAction(
|
|
||||||
context: context,
|
|
||||||
emailAddress: EmailAddress(null, value)
|
|
||||||
);
|
|
||||||
_gapBetweenTagChangedAndFindSuggestion = Timer(
|
|
||||||
const Duration(seconds: 1),
|
|
||||||
_handleGapBetweenTagChangedAndFindSuggestion
|
|
||||||
);
|
|
||||||
},
|
|
||||||
findSuggestions: _findSuggestions,
|
findSuggestions: _findSuggestions,
|
||||||
suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) {
|
suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -162,7 +149,8 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
selectedContactCallbackAction: (contact) {
|
selectedContactCallbackAction: (contact) {
|
||||||
_addEmailAddressToInputFieldAction(
|
_addEmailAddressToInputFieldAction(
|
||||||
context: context,
|
context: context,
|
||||||
emailAddress: contact);
|
emailAddress: contact
|
||||||
|
);
|
||||||
tagEditorState.closeSuggestionBox();
|
tagEditorState.closeSuggestionBox();
|
||||||
tagEditorState.resetTextField();
|
tagEditorState.resetTextField();
|
||||||
},
|
},
|
||||||
@@ -211,10 +199,6 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<List<SuggestionEmailAddress>> _findSuggestions(String query) async {
|
FutureOr<List<SuggestionEmailAddress>> _findSuggestions(String query) async {
|
||||||
if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
final processedQuery = query.trim();
|
final processedQuery = query.trim();
|
||||||
|
|
||||||
if (processedQuery.isEmpty) {
|
if (processedQuery.isEmpty) {
|
||||||
@@ -260,17 +244,7 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
.map((emailAddress) => SuggestionEmailAddress(emailAddress, state: SuggestionEmailState.duplicated));
|
.map((emailAddress) => SuggestionEmailAddress(emailAddress, state: SuggestionEmailState.duplicated));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleGapBetweenTagChangedAndFindSuggestion() {
|
bool _validateListEmailAddressIsValid(List<EmailAddress> listEmailAddress) => listEmailAddress.every(_validateEmailAddressIsValid);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAddRecipientButton(BuildContext context, {double? maxWidth}) {
|
Widget _buildAddRecipientButton(BuildContext context, {double? maxWidth}) {
|
||||||
return MaterialTextIconButton(
|
return MaterialTextIconButton(
|
||||||
@@ -281,80 +255,167 @@ class _AutocompleteContactTextFieldWithTagsState extends State<AutocompleteConta
|
|||||||
labelColor: Colors.white,
|
labelColor: Colors.white,
|
||||||
iconColor: Colors.white,
|
iconColor: Colors.white,
|
||||||
minimumSize: Size(maxWidth ?? 167, PlatformInfo.isMobile ? 44 : 54),
|
minimumSize: Size(maxWidth ?? 167, PlatformInfo.isMobile ? 44 : 54),
|
||||||
onTap: _handleAddRecipientAction
|
onTap: () => _handleAddRecipientAction(context)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleAddRecipientAction() {
|
void _handleAddRecipientAction(BuildContext context) {
|
||||||
_hideKeyboardForMobile();
|
KeyboardUtils.hideKeyboard(context);
|
||||||
if (widget.controller?.text.isNotEmpty == true) {
|
|
||||||
if (!_isDuplicatedRecipient(widget.controller?.text ?? '')) {
|
final inputText = widget.controller?.text ?? '';
|
||||||
_addEmailAddressToInputFieldAction(
|
|
||||||
context: context,
|
if (inputText.isNotEmpty) {
|
||||||
emailAddress: EmailAddress(null, widget.controller?.text)
|
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();
|
_closeSuggestionBox();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listEmailAddress.isEmpty) {
|
if (listEmailAddress.isEmpty) {
|
||||||
widget.onExceptionCallback?.call(true);
|
widget.onExceptionCallback?.call(RecipientListIsEmptyException());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_isValidAllEmailAddress(listEmailAddress) && _inputFieldIsEmpty()) {
|
if (!_validateListEmailAddressIsValid(listEmailAddress)) {
|
||||||
widget.onAddContactCallback?.call(List.from(listEmailAddress));
|
widget.onExceptionCallback?.call(RecipientListWithInvalidEmailsException());
|
||||||
setState(() {
|
return;
|
||||||
widget.controller?.clear();
|
|
||||||
listEmailAddress.clear();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
widget.onExceptionCallback?.call(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
widget.onAddContactCallback?.call(List.from(listEmailAddress));
|
||||||
|
|
||||||
|
_resetInputText();
|
||||||
|
setState(listEmailAddress.clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _closeSuggestionBox() {
|
bool _validateEmailAddressIsValid(EmailAddress emailAddress) {
|
||||||
keyToEmailTagEditor.currentState?.resetTextField();
|
return GetUtils.isEmail(emailAddress.emailAddress)
|
||||||
keyToEmailTagEditor.currentState?.closeSuggestionBox();
|
|| AppUtils.isEmailLocalhost(emailAddress.emailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _hideKeyboardForMobile() {
|
void _validateEmailAddressSameDomain({
|
||||||
if (!_responsiveUtils.isDesktop(context)) {
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addEmailAddressToInputFieldAction({
|
|
||||||
required BuildContext context,
|
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)) {
|
if (_isDuplicatedRecipient(emailAddress.emailAddress)) {
|
||||||
|
duplicatedRecipientAction?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final validateSameDomain = EmailUtils.isSameDomain(
|
bool isSameDomain = EmailUtils.isSameDomain(
|
||||||
emailAddress: emailAddress.emailAddress,
|
emailAddress: emailAddress.emailAddress,
|
||||||
internalDomain: widget.internalDomain
|
internalDomain: widget.internalDomain
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!validateSameDomain) {
|
if (isSameDomain) {
|
||||||
|
sameDomainAction?.call();
|
||||||
|
} else {
|
||||||
_showWarningDialogWithExternalDomain(
|
_showWarningDialogWithExternalDomain(
|
||||||
context: context,
|
context: context,
|
||||||
confirmAction: () {
|
confirmAction: confirmAction,
|
||||||
keyToEmailTagEditor.currentState?.resetTextField();
|
cancelAction: cancelAction
|
||||||
setState(() => listEmailAddress.add(emailAddress));
|
|
||||||
},
|
|
||||||
cancelAction: () {
|
|
||||||
keyToEmailTagEditor.currentState?.resetTextField();
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} 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({
|
void _showWarningDialogWithExternalDomain({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
VoidCallback? confirmAction,
|
VoidCallback? confirmAction,
|
||||||
|
|||||||
Reference in New Issue
Block a user