TF-3189 composer now correctly encodes subaddresses

This commit is contained in:
Florent Azavant
2024-11-15 14:01:33 +01:00
committed by Dat H. Pham
parent 74fc8d5d72
commit 2a56764214
6 changed files with 264 additions and 105 deletions
@@ -61,6 +61,7 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/get_outb
import 'package:tmail_ui_user/features/composer/presentation/extensions/get_sent_mailbox_id_for_composer_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_identities_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_shared_media_file_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/mail_address_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/mixin/drag_drog_file_mixin.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
@@ -1634,33 +1635,18 @@ class ComposerController extends BaseController
final inputCcEmail = ccEmailAddressController.text;
final inputBccEmail = bccEmailAddressController.text;
final inputReplyToEmail = replyToEmailAddressController.text;
log('ComposerController::_autoCreateEmailTag:inputToEmail = $inputToEmail | inputCcEmail = $inputCcEmail | inputBccEmail = $inputBccEmail');
if (inputToEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
prefixEmail: PrefixEmailAddress.to,
inputText: inputToEmail,
listEmailAddress: listToEmailAddress,
keyEmailTagEditor: keyToEmailTagEditor,
);
if (inputToEmail.isNotEmpty) {
_autoCreateToEmailTag(MailAddress.validateAddress(inputToEmail));
}
if (inputCcEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
prefixEmail: PrefixEmailAddress.cc,
inputText: inputCcEmail,
listEmailAddress: listCcEmailAddress,
keyEmailTagEditor: keyCcEmailTagEditor,
);
if (inputCcEmail.isNotEmpty) {
_autoCreateCcEmailTag(MailAddress.validateAddress(inputCcEmail));
}
if (inputBccEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
prefixEmail: PrefixEmailAddress.bcc,
inputText: inputBccEmail,
listEmailAddress: listBccEmailAddress,
keyEmailTagEditor: keyBccEmailTagEditor,
);
if (inputBccEmail.isNotEmpty) {
_autoCreateBccEmailTag(MailAddress.validateAddress(inputBccEmail));
}
if (inputReplyToEmail.isNotEmpty) {
_autoCreateReplyToEmailTag(inputReplyToEmail);
_autoCreateReplyToEmailTag(MailAddress.validateAddress(inputReplyToEmail));
}
}
@@ -1671,59 +1657,49 @@ class ComposerController extends BaseController
.contains(inputEmail);
}
void _autoCreateEmailTagForRecipientField({
required PrefixEmailAddress prefixEmail,
required String inputText,
required List<EmailAddress> listEmailAddress,
required GlobalKey<TagsEditorState> keyEmailTagEditor,
}) {
log('ComposerController::_autoCreateEmailTagForRecipientField:prefixEmail = $prefixEmail | inputText = $inputText | listEmailAddress = $listEmailAddress');
switch(prefixEmail) {
case PrefixEmailAddress.to:
case PrefixEmailAddress.cc:
case PrefixEmailAddress.bcc:
final listString = StringConvert.extractStrings(inputText).toSet();
if (listString.isEmpty && !_isDuplicatedRecipient(inputText, listEmailAddress)) {
final emailAddress = EmailAddress(null, inputText);
listEmailAddress.add(emailAddress);
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
keyEmailTagEditor.currentState?.resetTextField();
Future.delayed(
const Duration(milliseconds: 300),
keyEmailTagEditor.currentState?.closeSuggestionBox,
);
} else if (listString.isNotEmpty) {
final listStringNotExist = listString
.where((text) => !_isDuplicatedRecipient(text, listEmailAddress))
.toList();
if (listStringNotExist.isNotEmpty) {
final listAddress = listStringNotExist
.map((value) => EmailAddress(null, value))
.toList();
listEmailAddress.addAll(listAddress);
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
keyEmailTagEditor.currentState?.resetTextField();
Future.delayed(
const Duration(milliseconds: 300),
keyEmailTagEditor.currentState?.closeSuggestionBox,
);
}
}
break;
default:
break;
void _autoCreateToEmailTag(MailAddress inputMailAddress) {
if (!_isDuplicatedRecipient(inputMailAddress.asEncodedString(), listToEmailAddress)) {
listToEmailAddress.add(inputMailAddress.asEmailAddress());
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
}
log('ComposerController::_autoCreateToEmailTag(): STATE: ${keyToEmailTagEditor.currentState}');
keyToEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () {
keyToEmailTagEditor.currentState?.closeSuggestionBox();
});
}
void _autoCreateReplyToEmailTag(String inputEmail) {
if (!_isDuplicatedRecipient(inputEmail, listReplyToEmailAddress)) {
final emailAddress = EmailAddress(null, inputEmail);
listReplyToEmailAddress.add(emailAddress);
void _autoCreateCcEmailTag(MailAddress inputMailAddress) {
if (!_isDuplicatedRecipient(inputMailAddress.asEncodedString(), listCcEmailAddress)) {
listCcEmailAddress.add(inputMailAddress.asEmailAddress());
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
}
keyCcEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () {
keyCcEmailTagEditor.currentState?.closeSuggestionBox();
});
}
void _autoCreateBccEmailTag(MailAddress inputMailAddress) {
if (!_isDuplicatedRecipient(inputMailAddress.asEncodedString(), listBccEmailAddress)) {
listBccEmailAddress.add(inputMailAddress.asEmailAddress());
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
}
keyBccEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () {
keyBccEmailTagEditor.currentState?.closeSuggestionBox();
});
}
void _autoCreateReplyToEmailTag(MailAddress inputMailAddress) {
if (!_isDuplicatedRecipient(inputMailAddress.asEncodedString(), listReplyToEmailAddress)) {
listReplyToEmailAddress.add(inputMailAddress.asEmailAddress());
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
@@ -1801,44 +1777,29 @@ class ComposerController extends BaseController
case PrefixEmailAddress.to:
toAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputToEmail = toEmailAddressController.text;
if (inputToEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
prefixEmail: PrefixEmailAddress.to,
inputText: inputToEmail,
listEmailAddress: listToEmailAddress,
keyEmailTagEditor: keyToEmailTagEditor,
);
if (inputToEmail.isNotEmpty) {
_autoCreateToEmailTag(MailAddress.validateAddress(inputToEmail));
}
break;
case PrefixEmailAddress.cc:
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputCcEmail = ccEmailAddressController.text;
if (inputCcEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
prefixEmail: PrefixEmailAddress.cc,
inputText: inputCcEmail,
listEmailAddress: listCcEmailAddress,
keyEmailTagEditor: keyCcEmailTagEditor,
);
if (inputCcEmail.isNotEmpty) {
_autoCreateCcEmailTag(MailAddress.validateAddress(inputCcEmail));
}
break;
case PrefixEmailAddress.bcc:
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputBccEmail = bccEmailAddressController.text;
if (inputBccEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
prefixEmail: PrefixEmailAddress.bcc,
inputText: inputBccEmail,
listEmailAddress: listBccEmailAddress,
keyEmailTagEditor: keyBccEmailTagEditor,
);
if (inputBccEmail.isNotEmpty) {
_autoCreateBccEmailTag(MailAddress.validateAddress(inputBccEmail));
}
break;
case PrefixEmailAddress.replyTo:
replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputReplyToEmail = replyToEmailAddressController.text;
if (inputReplyToEmail.isNotEmpty) {
_autoCreateReplyToEmailTag(inputReplyToEmail);
_autoCreateReplyToEmailTag(MailAddress.validateAddress(inputReplyToEmail));
}
break;
default:
@@ -0,0 +1,17 @@
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:core/core.dart';
extension MailAddressExtension on MailAddress {
String? get getDisplayName {
String? localPartDetails = getLocalPartDetails();
if(localPartDetails == null) {
return null;
} else {
return '${getLocalPartWithoutDetails()} [${getLocalPartDetails()}]';
}
}
EmailAddress asEmailAddress() {
return EmailAddress(getDisplayName, asEncodedString());
}
}
@@ -8,6 +8,7 @@ import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/mail/mail_address.dart';
import 'package:core/utils/platform_info.dart';
import 'package:core/utils/string_convert.dart';
import 'package:flutter/material.dart';
@@ -18,6 +19,7 @@ import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/expand_mode.dart';
import 'package:super_tag_editor/tag_editor.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/mail_address_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
@@ -228,7 +230,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
return RecipientSuggestionItemWidget(
imagePaths: widget.imagePaths,
suggestionState: suggestionEmailAddress.state,
emailAddress: suggestionEmailAddress.emailAddress,
emailAddress: MailAddress.validateAddress(suggestionEmailAddress.emailAddress.emailAddress).asEmailAddress(),
suggestionValid: suggestionValid,
highlight: highlight,
onSelectedAction: (emailAddress) {
@@ -319,7 +321,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
return RecipientSuggestionItemWidget(
imagePaths: widget.imagePaths,
suggestionState: suggestionEmailAddress.state,
emailAddress: suggestionEmailAddress.emailAddress,
emailAddress: MailAddress.validateAddress(suggestionEmailAddress.emailAddress.emailAddress).asEmailAddress(),
suggestionValid: suggestionValid,
highlight: highlight,
onSelectedAction: (emailAddress) {
@@ -523,8 +525,9 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
SuggestionEmailAddress suggestionEmailAddress,
StateSetter stateSetter
) {
if (!_isDuplicatedRecipient(suggestionEmailAddress.emailAddress.emailAddress)) {
stateSetter(() => _currentListEmailAddress.add(suggestionEmailAddress.emailAddress));
MailAddress mailAddress = MailAddress.validateAddress(suggestionEmailAddress.emailAddress.emailAddress);
if (!_isDuplicatedRecipient(mailAddress.asEncodedString())) {
stateSetter(() => _currentListEmailAddress.add(mailAddress.asEmailAddress()));
_updateListEmailAddressAction();
}
}
@@ -536,9 +539,10 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
void _createMailTag(String value, StateSetter stateSetter) {
final listString = StringConvert.extractStrings(value.trim()).toSet();
MailAddress mailAddress = MailAddress.validateAddress(value.trim());
if (listString.isEmpty && !_isDuplicatedRecipient(value)) {
stateSetter(() => _currentListEmailAddress.add(EmailAddress(null, value)));
if (listString.isEmpty && !_isDuplicatedRecipient(mailAddress.asEncodedString())) {
stateSetter(() => _currentListEmailAddress.add(mailAddress.asEmailAddress()));
_updateListEmailAddressAction();
} else if (listString.isNotEmpty) {
final listStringNotExist = listString
@@ -560,9 +564,9 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
String value,
StateSetter stateSetter
) {
final textTrim = value.trim();
if (!_isDuplicatedRecipient(textTrim)) {
stateSetter(() => _currentListEmailAddress.add(EmailAddress(null, textTrim)));
MailAddress mailAddress = MailAddress.validateAddress(value.trim());
if (!_isDuplicatedRecipient(mailAddress.asEncodedString())) {
stateSetter(() => _currentListEmailAddress.add(mailAddress.asEmailAddress()));
_updateListEmailAddressAction();
}
_gapBetweenTagChangedAndFindSuggestion = Timer(
@@ -86,7 +86,8 @@ class EmailUtils {
static bool isEmailAddressValid(String address) {
try {
return GetUtils.isEmail(address) && MailAddress.validateAddress(address).asString().isNotEmpty;
MailAddress mailAddress = MailAddress.validateAddress(address);
return GetUtils.isEmail(mailAddress.stripDetails().asString()) && mailAddress.asString().isNotEmpty;
} catch(e) {
logError('EmailUtils::isEmailAddressValid: Exception = $e');
return false;