Handle the behaviour of Reply/ReplyToList/ReplyAll button

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-02-18 11:50:27 +07:00
committed by Dat H. Pham
parent b8b70d26d4
commit bcad1e0cd9
9 changed files with 601 additions and 227 deletions
@@ -55,13 +55,13 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/restore_email_in
import 'package:tmail_ui_user/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/auto_create_tag_for_recipients_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/get_draft_mailbox_id_for_composer_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/get_draft_mailbox_id_for_composer_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/get_outbox_mailbox_id_for_composer_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/get_outbox_mailbox_id_for_composer_extension.dart';
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/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_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/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/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/create_email_request.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
@@ -461,7 +461,7 @@ class ComposerController extends BaseController
} }
Future<void> _saveComposerCacheOnWebAction() async { Future<void> _saveComposerCacheOnWebAction() async {
_autoCreateEmailTag(); autoCreateEmailTag();
final createEmailRequest = await _generateCreateEmailRequest(); final createEmailRequest = await _generateCreateEmailRequest();
if (createEmailRequest == null) return; if (createEmailRequest == null) return;
@@ -551,7 +551,7 @@ class ComposerController extends BaseController
richTextMobileTabletController?.richTextController.hideRichTextView(); richTextMobileTabletController?.richTextController.hideRichTextView();
} }
_collapseAllRecipient(); _collapseAllRecipient();
_autoCreateEmailTag(); autoCreateEmailTag();
} }
} }
@@ -660,7 +660,7 @@ class ComposerController extends BaseController
listToEmailAddress.addAll(arguments.listEmailAddress ?? []); listToEmailAddress.addAll(arguments.listEmailAddress ?? []);
isInitialRecipient.value = true; isInitialRecipient.value = true;
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
break; break;
case EmailActionType.composeFromMailtoUri: case EmailActionType.composeFromMailtoUri:
if (arguments.subject != null) { if (arguments.subject != null) {
@@ -683,7 +683,7 @@ class ComposerController extends BaseController
listBccEmailAddress = arguments.bcc!; listBccEmailAddress = arguments.bcc!;
} }
_getEmailContentFromMailtoUri(arguments.body ?? ''); _getEmailContentFromMailtoUri(arguments.body ?? '');
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
break; break;
case EmailActionType.reply: case EmailActionType.reply:
case EmailActionType.replyToList: case EmailActionType.replyToList:
@@ -760,7 +760,7 @@ class ComposerController extends BaseController
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
} }
_getEmailContentFromUnsubscribeMailtoLink(arguments.body ?? ''); _getEmailContentFromUnsubscribeMailtoLink(arguments.body ?? '');
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
break; break;
default: default:
break; break;
@@ -891,7 +891,7 @@ class ComposerController extends BaseController
replyToAddressExpandMode.value = ExpandMode.COLLAPSE; replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
} }
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
} }
void updateListEmailAddress( void updateListEmailAddress(
@@ -914,10 +914,10 @@ class ComposerController extends BaseController
default: default:
break; break;
} }
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
} }
void _updateStatusEmailSendButton() { void updateStatusEmailSendButton() {
if (listToEmailAddress.isNotEmpty if (listToEmailAddress.isNotEmpty
|| listCcEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty
|| listBccEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty
@@ -942,7 +942,7 @@ class ComposerController extends BaseController
|| bccEmailAddressController.text.isNotEmpty || bccEmailAddressController.text.isNotEmpty
|| replyToEmailAddressController.text.isNotEmpty) { || replyToEmailAddressController.text.isNotEmpty) {
_collapseAllRecipient(); _collapseAllRecipient();
_autoCreateEmailTag(); autoCreateEmailTag();
} }
if (!isEnableEmailSendButton.value) { if (!isEnableEmailSendButton.value) {
@@ -1631,86 +1631,6 @@ class ComposerController extends BaseController
replyToAddressExpandMode.value = ExpandMode.COLLAPSE; replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
} }
void _autoCreateEmailTag() {
final inputToEmail = toEmailAddressController.text;
final inputCcEmail = ccEmailAddressController.text;
final inputBccEmail = bccEmailAddressController.text;
final inputReplyToEmail = replyToEmailAddressController.text;
if (inputToEmail.isNotEmpty) {
_autoCreateToEmailTag(MailAddress.validateAddress(inputToEmail));
}
if (inputCcEmail.isNotEmpty) {
_autoCreateCcEmailTag(MailAddress.validateAddress(inputCcEmail));
}
if (inputBccEmail.isNotEmpty) {
_autoCreateBccEmailTag(MailAddress.validateAddress(inputBccEmail));
}
if (inputReplyToEmail.isNotEmpty) {
_autoCreateReplyToEmailTag(MailAddress.validateAddress(inputReplyToEmail));
}
}
bool _isDuplicatedRecipient(String inputEmail, List<EmailAddress> listEmailAddress) {
return listEmailAddress
.map((emailAddress) => emailAddress.email)
.whereNotNull()
.contains(inputEmail);
}
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 _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();
}
keyReplyToEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () {
keyReplyToEmailTagEditor.currentState?.closeSuggestionBox();
});
}
void _closeSuggestionBox() { void _closeSuggestionBox() {
if (toEmailAddressController.text.isEmpty) { if (toEmailAddressController.text.isEmpty) {
keyToEmailTagEditor.currentState?.closeSuggestionBox(); keyToEmailTagEditor.currentState?.closeSuggestionBox();
@@ -1778,29 +1698,29 @@ class ComposerController extends BaseController
case PrefixEmailAddress.to: case PrefixEmailAddress.to:
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputToEmail = toEmailAddressController.text; final inputToEmail = toEmailAddressController.text;
if (inputToEmail.isNotEmpty) { if (inputToEmail.trim().isNotEmpty) {
_autoCreateToEmailTag(MailAddress.validateAddress(inputToEmail)); autoCreateEmailTagForType(PrefixEmailAddress.to, inputToEmail);
} }
break; break;
case PrefixEmailAddress.cc: case PrefixEmailAddress.cc:
ccAddressExpandMode.value = ExpandMode.COLLAPSE; ccAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputCcEmail = ccEmailAddressController.text; final inputCcEmail = ccEmailAddressController.text;
if (inputCcEmail.isNotEmpty) { if (inputCcEmail.trim().isNotEmpty) {
_autoCreateCcEmailTag(MailAddress.validateAddress(inputCcEmail)); autoCreateEmailTagForType(PrefixEmailAddress.cc, inputCcEmail);
} }
break; break;
case PrefixEmailAddress.bcc: case PrefixEmailAddress.bcc:
bccAddressExpandMode.value = ExpandMode.COLLAPSE; bccAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputBccEmail = bccEmailAddressController.text; final inputBccEmail = bccEmailAddressController.text;
if (inputBccEmail.isNotEmpty) { if (inputBccEmail.trim().isNotEmpty) {
_autoCreateBccEmailTag(MailAddress.validateAddress(inputBccEmail)); autoCreateEmailTagForType(PrefixEmailAddress.bcc, inputBccEmail);
} }
break; break;
case PrefixEmailAddress.replyTo: case PrefixEmailAddress.replyTo:
replyToAddressExpandMode.value = ExpandMode.COLLAPSE; replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputReplyToEmail = replyToEmailAddressController.text; final inputReplyToEmail = replyToEmailAddressController.text;
if (inputReplyToEmail.isNotEmpty) { if (inputReplyToEmail.trim().isNotEmpty) {
_autoCreateReplyToEmailTag(MailAddress.validateAddress(inputReplyToEmail)); autoCreateEmailTagForType(PrefixEmailAddress.replyTo, inputReplyToEmail);
} }
break; break;
default: default:
@@ -1866,7 +1786,7 @@ class ComposerController extends BaseController
ccAddressExpandMode.value = ExpandMode.COLLAPSE; ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.value = ExpandMode.COLLAPSE; bccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.refresh(); bccAddressExpandMode.refresh();
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
} }
void _removeBccEmailAddressFromFormerIdentity(Set<EmailAddress> listEmailAddress) { void _removeBccEmailAddressFromFormerIdentity(Set<EmailAddress> listEmailAddress) {
@@ -1879,7 +1799,7 @@ class ComposerController extends BaseController
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
ccAddressExpandMode.value = ExpandMode.COLLAPSE; ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.value = ExpandMode.COLLAPSE; bccAddressExpandMode.value = ExpandMode.COLLAPSE;
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
} }
Future<void> _applySignature(String signature) async { Future<void> _applySignature(String signature) async {
@@ -1952,7 +1872,7 @@ class ComposerController extends BaseController
richTextMobileTabletController?.richTextController.showDeviceKeyboard); richTextMobileTabletController?.richTextController.showDeviceKeyboard);
} }
_collapseAllRecipient(); _collapseAllRecipient();
_autoCreateEmailTag(); autoCreateEmailTag();
} }
void _onChangeCursorOnMobile(List<int>? coordinates, BuildContext context) { void _onChangeCursorOnMobile(List<int>? coordinates, BuildContext context) {
@@ -2083,7 +2003,7 @@ class ComposerController extends BaseController
void handleOnMouseDownHtmlEditorWeb() { void handleOnMouseDownHtmlEditorWeb() {
_collapseAllRecipient(); _collapseAllRecipient();
_autoCreateEmailTag(); autoCreateEmailTag();
} }
FocusNode? getNextFocusOfToEmailAddress() { FocusNode? getNextFocusOfToEmailAddress() {
@@ -2117,7 +2037,7 @@ class ComposerController extends BaseController
} }
void handleFocusNextAddressAction() { void handleFocusNextAddressAction() {
_autoCreateEmailTag(); autoCreateEmailTag();
} }
bool get isNetworkConnectionAvailable => networkConnectionController.isNetworkConnectionAvailable(); bool get isNetworkConnectionAvailable => networkConnectionController.isNetworkConnectionAvailable();
@@ -2176,7 +2096,7 @@ class ComposerController extends BaseController
isInitialRecipient.value = true; isInitialRecipient.value = true;
isInitialRecipient.refresh(); isInitialRecipient.refresh();
_updateStatusEmailSendButton(); updateStatusEmailSendButton();
} }
void onAttachmentDropZoneListener(Attachment attachment) { void onAttachmentDropZoneListener(Attachment attachment) {
@@ -0,0 +1,74 @@
import 'package:core/utils/mail/mail_address.dart';
import 'package:flutter/cupertino.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/email/prefix_email_address.dart';
import 'package:super_tag_editor/tag_editor.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/mail_address_extension.dart';
extension AutoCreateTagForRecipientsExtension on ComposerController {
void autoCreateEmailTag() {
final emailInputs = {
PrefixEmailAddress.to: toEmailAddressController.text,
PrefixEmailAddress.cc: ccEmailAddressController.text,
PrefixEmailAddress.bcc: bccEmailAddressController.text,
PrefixEmailAddress.replyTo: replyToEmailAddressController.text,
};
emailInputs.forEach((prefix, input) {
if (input.trim().isNotEmpty) {
autoCreateEmailTagForType(prefix, input);
}
});
}
Map<PrefixEmailAddress, List<EmailAddress>> get _emailLists => {
PrefixEmailAddress.to: listToEmailAddress,
PrefixEmailAddress.cc: listCcEmailAddress,
PrefixEmailAddress.bcc: listBccEmailAddress,
PrefixEmailAddress.replyTo: listReplyToEmailAddress,
};
Map<PrefixEmailAddress, GlobalKey<TagsEditorState>> get _emailEditors => {
PrefixEmailAddress.to: keyToEmailTagEditor,
PrefixEmailAddress.cc: keyCcEmailTagEditor,
PrefixEmailAddress.bcc: keyBccEmailTagEditor,
PrefixEmailAddress.replyTo: keyReplyToEmailTagEditor,
};
void autoCreateEmailTagForType(PrefixEmailAddress type, String input) {
final emailAddressRecord = _generateEmailAddressFromString(input);
final emailList = _emailLists[type]!;
final keyEditor = _emailEditors[type]!;
final emailSet = emailList.toSet();
if (emailSet.any((email) => email.email == emailAddressRecord.$1)) return;
emailSet.add(emailAddressRecord.$2);
emailList
..clear()
..addAll(emailSet);
isInitialRecipient.value = true;
isInitialRecipient.refresh();
updateStatusEmailSendButton();
keyEditor.currentState?.resetTextField();
Future.delayed(
const Duration(milliseconds: 300),
keyEditor.currentState?.closeSuggestionBox,
);
}
(String email, EmailAddress emailAddress) _generateEmailAddressFromString(String input) {
try {
final mailAddress = MailAddress.validateAddress(input);
return (mailAddress.asEncodedString(), mailAddress.asEmailAddress());
} catch (e) {
return (input, EmailAddress(null, input));
}
}
}
@@ -2,7 +2,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
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/responsive_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
@@ -230,7 +229,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
return RecipientSuggestionItemWidget( return RecipientSuggestionItemWidget(
imagePaths: widget.imagePaths, imagePaths: widget.imagePaths,
suggestionState: suggestionEmailAddress.state, suggestionState: suggestionEmailAddress.state,
emailAddress: MailAddress.validateAddress(suggestionEmailAddress.emailAddress.emailAddress).asEmailAddress(), emailAddress: _subAddressingValidatedEmailAddress(suggestionEmailAddress.emailAddress),
suggestionValid: suggestionValid, suggestionValid: suggestionValid,
highlight: highlight, highlight: highlight,
onSelectedAction: (emailAddress) { onSelectedAction: (emailAddress) {
@@ -321,7 +320,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
return RecipientSuggestionItemWidget( return RecipientSuggestionItemWidget(
imagePaths: widget.imagePaths, imagePaths: widget.imagePaths,
suggestionState: suggestionEmailAddress.state, suggestionState: suggestionEmailAddress.state,
emailAddress: MailAddress.validateAddress(suggestionEmailAddress.emailAddress.emailAddress).asEmailAddress(), emailAddress: _subAddressingValidatedEmailAddress(suggestionEmailAddress.emailAddress),
suggestionValid: suggestionValid, suggestionValid: suggestionValid,
highlight: highlight, highlight: highlight,
onSelectedAction: (emailAddress) { onSelectedAction: (emailAddress) {
@@ -466,13 +465,10 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
} }
bool _isDuplicatedRecipient(String inputEmail) { bool _isDuplicatedRecipient(String inputEmail) {
if (inputEmail.isEmpty) { if (inputEmail.trim().isEmpty) return false;
return false;
}
return _currentListEmailAddress return _currentListEmailAddress
.map((emailAddress) => emailAddress.email) .any((emailAddress) => emailAddress.email?.trim() == inputEmail.trim());
.whereNotNull()
.contains(inputEmail);
} }
SuggestionEmailAddress _toSuggestionEmailAddress(EmailAddress item, List<EmailAddress> addedEmailAddresses) { SuggestionEmailAddress _toSuggestionEmailAddress(EmailAddress item, List<EmailAddress> addedEmailAddresses) {
@@ -524,13 +520,10 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
void _handleSelectOptionAction( void _handleSelectOptionAction(
SuggestionEmailAddress suggestionEmailAddress, SuggestionEmailAddress suggestionEmailAddress,
StateSetter stateSetter StateSetter stateSetter
) { ) => _onEmailAddressReceived(
MailAddress mailAddress = MailAddress.validateAddress(suggestionEmailAddress.emailAddress.emailAddress); suggestionEmailAddress.emailAddress.emailAddress,
if (!_isDuplicatedRecipient(mailAddress.asEncodedString())) { stateSetter,
stateSetter(() => _currentListEmailAddress.add(mailAddress.asEmailAddress())); );
_updateListEmailAddressAction();
}
}
void _handleSubmitTagAction( void _handleSubmitTagAction(
String value, String value,
@@ -539,11 +532,9 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
void _createMailTag(String value, StateSetter stateSetter) { void _createMailTag(String value, StateSetter stateSetter) {
final listString = StringConvert.extractStrings(value.trim()).toSet(); final listString = StringConvert.extractStrings(value.trim()).toSet();
MailAddress mailAddress = MailAddress.validateAddress(value.trim());
if (listString.isEmpty && !_isDuplicatedRecipient(mailAddress.asEncodedString())) { if (listString.isEmpty && !_isDuplicatedRecipient(value)) {
stateSetter(() => _currentListEmailAddress.add(mailAddress.asEmailAddress())); _onEmailAddressReceived(value, stateSetter);
_updateListEmailAddressAction();
} else if (listString.isNotEmpty) { } else if (listString.isNotEmpty) {
final listStringNotExist = listString final listStringNotExist = listString
.where((text) => !_isDuplicatedRecipient(text)) .where((text) => !_isDuplicatedRecipient(text))
@@ -564,11 +555,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
String value, String value,
StateSetter stateSetter StateSetter stateSetter
) { ) {
MailAddress mailAddress = MailAddress.validateAddress(value.trim()); _onEmailAddressReceived(value, stateSetter);
if (!_isDuplicatedRecipient(mailAddress.asEncodedString())) {
stateSetter(() => _currentListEmailAddress.add(mailAddress.asEmailAddress()));
_updateListEmailAddressAction();
}
_gapBetweenTagChangedAndFindSuggestion = Timer( _gapBetweenTagChangedAndFindSuggestion = Timer(
const Duration(seconds: 1), const Duration(seconds: 1),
_handleGapBetweenTagChangedAndFindSuggestion _handleGapBetweenTagChangedAndFindSuggestion
@@ -608,4 +595,30 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
return null; return null;
} }
} }
void _onEmailAddressReceived(String input, StateSetter stateSetter) {
final emailAddressRecord = _generateEmailAddressFromString(input);
if (!_isDuplicatedRecipient(emailAddressRecord.$1)) {
stateSetter(() => _currentListEmailAddress.add(emailAddressRecord.$2));
_updateListEmailAddressAction();
}
}
EmailAddress _subAddressingValidatedEmailAddress(EmailAddress emailAddress) {
try {
final mailAddress = MailAddress.validateAddress(emailAddress.emailAddress);
return mailAddress.asEmailAddress();
} catch (e) {
return emailAddress;
}
}
(String email, EmailAddress emailAddress) _generateEmailAddressFromString(String input) {
try {
final mailAddress = MailAddress.validateAddress(input);
return (mailAddress.asEncodedString(), mailAddress.asEmailAddress());
} catch (e) {
return (input, EmailAddress(null, input));
}
}
} }
@@ -21,85 +21,84 @@ extension PresentationEmailExtension on PresentationEmail {
switch (emailActionType) { switch (emailActionType) {
case EmailActionType.reply: case EmailActionType.reply:
List<EmailAddress> listReplyAddress; return _handleReply(
if (newReplyToAddress.isNotEmpty) { isSender: isSender,
listReplyAddress = newReplyToAddress; newToAddress: newToAddress,
} else if (isSender) { newFromAddress: newFromAddress,
listReplyAddress = newToAddress; newReplyToAddress: newReplyToAddress,
} else { userName: userName,
listReplyAddress = newFromAddress; );
}
final listReplyAddressWithoutUsername = listReplyAddress.withoutMe(userName);
return Tuple4(listReplyAddressWithoutUsername, [], [], []);
case EmailActionType.replyToList: case EmailActionType.replyToList:
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost ?? ''); return _handleReplyToList(listPost, userName);
final listToAddressWithoutUsername = recipientRecord.toMailAddresses
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
final listCcAddressWithoutUsername = recipientRecord.ccMailAddresses
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
final listBccAddressWithoutUsername = recipientRecord.bccMailAddresses
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
return Tuple4(
listToAddressWithoutUsername,
listCcAddressWithoutUsername,
listBccAddressWithoutUsername,
[]
);
case EmailActionType.replyAll: case EmailActionType.replyAll:
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost ?? ''); return _handleReplyAll(
isSender: isSender,
final listToAddress = recipientRecord.toMailAddresses newToAddress: newToAddress,
+ newReplyToAddress newCcAddress: newCcAddress,
+ newFromAddress newBccAddress: newBccAddress,
+ newToAddress; newReplyToAddress: newReplyToAddress,
final listCcAddress = recipientRecord.ccMailAddresses + newCcAddress; newFromAddress: newFromAddress,
final listBccAddress = recipientRecord.bccMailAddresses + newBccAddress; userName: userName,
final listToAddressWithoutUsername = listToAddress
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
final listCcAddressWithoutUsername = listCcAddress
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
final listBccAddressWithoutUsername = listBccAddress
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
return Tuple4(
listToAddressWithoutUsername,
listCcAddressWithoutUsername,
listBccAddressWithoutUsername,
[]
); );
case EmailActionType.editDraft:
return Tuple4(newToAddress, newCcAddress, newBccAddress, newReplyToAddress);
default: default:
final listToAddressWithoutUsername = newToAddress.withoutMe(userName); return Tuple4(newToAddress, newCcAddress, newBccAddress, newReplyToAddress);
final listCcAddressWithoutUsername = newCcAddress.withoutMe(userName);
final listBccAddressWithoutUsername = newBccAddress.withoutMe(userName);
return Tuple4(
listToAddressWithoutUsername,
listCcAddressWithoutUsername,
listBccAddressWithoutUsername,
[]
);
} }
} }
Tuple4<List<EmailAddress>, List<EmailAddress>, List<EmailAddress>, List<EmailAddress>> _handleReply({
required bool isSender,
required List<EmailAddress> newToAddress,
required List<EmailAddress> newFromAddress,
required List<EmailAddress> newReplyToAddress,
String? userName,
}) {
if (isSender) {
return Tuple4(newToAddress, [], [], newReplyToAddress);
}
final listToAddress = newReplyToAddress.isNotEmpty
? newReplyToAddress.withoutMe(userName)
: newFromAddress.withoutMe(userName);
return Tuple4(listToAddress, [], [], []);
}
Tuple4<List<EmailAddress>, List<EmailAddress>, List<EmailAddress>, List<EmailAddress>> _handleReplyToList(String? listPost, String? userName) {
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost ?? '');
return Tuple4(
recipientRecord.toMailAddresses.removeDuplicateEmails().withoutMe(userName),
recipientRecord.ccMailAddresses.removeDuplicateEmails().withoutMe(userName),
recipientRecord.bccMailAddresses.removeDuplicateEmails().withoutMe(userName),
[],
);
}
Tuple4<List<EmailAddress>, List<EmailAddress>, List<EmailAddress>, List<EmailAddress>> _handleReplyAll({
required bool isSender,
required List<EmailAddress> newToAddress,
required List<EmailAddress> newCcAddress,
required List<EmailAddress> newBccAddress,
required List<EmailAddress> newReplyToAddress,
required List<EmailAddress> newFromAddress,
String? userName,
}) {
if (isSender) {
return Tuple4(newToAddress, newCcAddress, newBccAddress, newReplyToAddress);
}
final listToAddress = {
...(newReplyToAddress.isNotEmpty ? newReplyToAddress : newFromAddress),
...newToAddress,
}.removeDuplicateEmails().withoutMe(userName);
return Tuple4(
listToAddress,
newCcAddress.withoutMe(userName),
newBccAddress.withoutMe(userName),
[],
);
}
} }
@@ -218,4 +218,11 @@ class EmailUtils {
return null; return null;
} }
} }
static bool isReplyToListEnabled(String listPost) {
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost);
return recipientRecord.toMailAddresses.isNotEmpty ||
recipientRecord.ccMailAddresses.isNotEmpty ||
recipientRecord.bccMailAddresses.isNotEmpty;
}
} }
@@ -9,6 +9,7 @@ import 'package:model/extensions/email_extension.dart';
import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/extensions/presentation_email_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_view_bottom_bar_widget_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_view_bottom_bar_widget_styles.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnEmailActionCallback = void Function(EmailActionType, PresentationEmail); typedef OnEmailActionCallback = void Function(EmailActionType, PresentationEmail);
@@ -76,9 +77,10 @@ class EmailViewBottomBarWidget extends StatelessWidget {
}), }),
Obx(() { Obx(() {
final currentEmailLoaded = _singleEmailController.currentEmailLoaded.value; final currentEmailLoaded = _singleEmailController.currentEmailLoaded.value;
final isReplyToListEnabled = EmailUtils.isReplyToListEnabled(
if (currentEmailLoaded != null && currentEmailLoaded?.emailCurrent?.listPost ?? '',
currentEmailLoaded.emailCurrent?.hasListPost == true) { );
if (currentEmailLoaded != null && isReplyToListEnabled) {
return Expanded( return Expanded(
child: TMailButtonWidget( child: TMailButtonWidget(
key: const Key('reply_to_list_email_button'), key: const Key('reply_to_list_email_button'),
+1 -3
View File
@@ -36,9 +36,7 @@ extension EmailExtension on Email {
String get sMimeStatusHeaderParsed => sMimeStatusHeader?[IndividualHeaderIdentifier.sMimeStatusHeader]?.trim() ?? ''; String get sMimeStatusHeaderParsed => sMimeStatusHeader?[IndividualHeaderIdentifier.sMimeStatusHeader]?.trim() ?? '';
String get listPost => headers.listPost; String get listPost => headers.listPost.trim();
bool get hasListPost => listPost.isNotEmpty;
IdentityId? get identityIdFromHeader { IdentityId? get identityIdFromHeader {
final rawIdentityId = identityHeader?[IndividualHeaderIdentifier.identityHeader]; final rawIdentityId = identityHeader?[IndividualHeaderIdentifier.identityHeader];
@@ -83,4 +83,17 @@ extension ListEmailAddressExtension on List<EmailAddress> {
return where((emailAddress) => emailAddress.emailAddress != userName).toList(); return where((emailAddress) => emailAddress.emailAddress != userName).toList();
} }
List<EmailAddress> removeDuplicateEmails() {
final seenEmails = <String>{};
return where((emailAddress) {
if (emailAddress.emailAddress.isEmpty ||
seenEmails.contains(emailAddress.emailAddress)) {
return false;
} else {
seenEmails.add(emailAddress.emailAddress);
return true;
}
}).toList();
}
} }
@@ -15,10 +15,22 @@ void main() {
final userEEmailAddress = EmailAddress('User E', 'userE@domain.com'); final userEEmailAddress = EmailAddress('User E', 'userE@domain.com');
final replyToEmailAddress = EmailAddress('Reply To', 'replyToThis@domain.com'); final replyToEmailAddress = EmailAddress('Reply To', 'replyToThis@domain.com');
final replyToListEmailAddress = EmailAddress(null, 'replyToList@domain.com'); final replyToListEmailAddress = EmailAddress(null, 'replyToList@domain.com');
final listPost = '<mailto:${replyToListEmailAddress.emailAddress}>';
group('GIVEN user A is the sender AND sends an email to user B and user E, cc to user C, bcc to user D', () { group(
test('THEN user A clicks reply, generateRecipientsEmailAddressForComposer SHOULD return user B email + user E email to reply', () { 'GIVEN user A is the sender\n'
final expectedResult = Tuple3([userBEmailAddress, userEEmailAddress], <EmailAddress>[], <EmailAddress>[]); 'AND sends an email to user B and user E, cc to user C, bcc to user D',
() {
test(
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user B email + user E email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userAEmailAddress}, from: {userAEmailAddress},
@@ -36,10 +48,19 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
test('THEN user A clicks reply all, generateRecipientsEmailAddressForComposer SHOULD return user B email + user E email to reply, user C email address to cc, user D email address to bcc', () { test(
final expectedResult = Tuple3([userBEmailAddress, userEEmailAddress], <EmailAddress>[userCEmailAddress], <EmailAddress>[userDEmailAddress]); 'THEN user A clicks reply all, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user B email + user E email to reply, user C email address to cc, user D email address to bcc',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[userCEmailAddress],
<EmailAddress>[userDEmailAddress],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userAEmailAddress}, from: {userAEmailAddress},
@@ -57,12 +78,294 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
}); });
group('GIVEN user B is the sender, SENDER configured the replyTo email AND send an email to user A and user E, cc to user C, bcc to user D', () { group(
test('THEN user A click reply, generateRecipientsEmailAddressForComposer SHOULD return only replyToEmailAddress email to reply' , () { 'GIVEN user A is the sender\n'
final expectedResult = Tuple3([replyToEmailAddress], <EmailAddress>[], <EmailAddress>[]); 'AND sends an email to user B and user E, cc to user C, bcc to user D',
() {
test(
'Email has Reply To and List-Post'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user B email + user E email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[replyToEmailAddress],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userEEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {replyToEmailAddress},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
listPost: listPost,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
test(
'Email has Reply To and not List-Post'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user B email + user E email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[replyToEmailAddress],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userEEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {replyToEmailAddress},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
test(
'Email has List-Post and not Reply To'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user B email + user E email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userEEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {replyToEmailAddress},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
listPost: listPost,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
test(
'Email has not Reply To and List-Post'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user B email + user E email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userEEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
});
group(
'GIVEN user A is the sender\n'
'AND sends an email to user A and user B, cc to user C, bcc to user D',
() {
test(
'Email has Reply To and List-Post'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user A email + user B email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userAEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[replyToEmailAddress],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userAEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {replyToEmailAddress},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
listPost: listPost,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
test(
'Email has Reply To and not List-Post'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user A email + user B email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userAEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[replyToEmailAddress],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userAEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {replyToEmailAddress},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
test(
'Email has List-Post and not Reply To'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user A email + user B email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userAEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userAEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {replyToEmailAddress},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
listPost: listPost,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
test(
'Email has not Reply To and List-Post'
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user A email + user B email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userAEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
[],
);
final emailToReply = PresentationEmail(
from: {userAEmailAddress},
to: {userBEmailAddress, userAEmailAddress},
cc: {userCEmailAddress},
bcc: {userDEmailAddress},
replyTo: {},
);
final result = emailToReply.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.reply,
isSender: true,
userName: userAEmailAddress.emailAddress,
);
expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
});
});
group(
'GIVEN user B is the sender, SENDER configured the replyTo email\n'
'AND send an email to user A and user E, cc to user C, bcc to user D',
() {
test(
'THEN user A click reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return only replyToEmailAddress email to reply' ,
() {
final expectedResult = Tuple4(
[replyToEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userBEmailAddress}, from: {userBEmailAddress},
@@ -80,10 +383,19 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
test('THEN user A clicks reply all, generateRecipientsEmailAddressForComposer SHOULD return replyToEmailAddress + user A email + user E email to reply, user C email address to cc, user D email address to bcc', () { test(
final expectedResult = Tuple3([userAEmailAddress, userEEmailAddress, replyToEmailAddress], <EmailAddress>[userCEmailAddress], <EmailAddress>[userDEmailAddress]); 'THEN user A clicks reply all, generateRecipientsEmailAddressForComposer\n'
'SHOULD return replyToEmailAddress + user A email + user E email to reply, user C email address to cc, user D email address to bcc',
() {
final expectedResult = Tuple4(
[userAEmailAddress, userEEmailAddress, replyToEmailAddress],
<EmailAddress>[userCEmailAddress],
<EmailAddress>[userDEmailAddress],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userBEmailAddress}, from: {userBEmailAddress},
@@ -101,12 +413,24 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
}); });
group('GIVEN user B is the sender, SENDER does not have the replyTo email AND sends an email to user A and user E, cc to user C, bcc to user D', () { group(
test('THEN user A clicks reply, generateRecipientsEmailAddressForComposer SHOULD return only user B email to reply', () { 'GIVEN user B is the sender, SENDER does not have the replyTo email\n'
final expectedResult = Tuple3([userBEmailAddress], <EmailAddress>[], <EmailAddress>[]); 'AND sends an email to user A and user E, cc to user C, bcc to user D',
() {
test(
'THEN user A clicks reply, generateRecipientsEmailAddressForComposer\n'
'SHOULD return only user B email to reply',
() {
final expectedResult = Tuple4(
[userBEmailAddress],
<EmailAddress>[],
<EmailAddress>[],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userBEmailAddress}, from: {userBEmailAddress},
@@ -123,10 +447,19 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
test('THEN user A clicks reply all, generateRecipientsEmailAddressForComposer SHOULD return user A email + user E email + user B email to reply, user C email to cc, user D email to bcc', () { test(
final expectedResult = Tuple3([userAEmailAddress, userEEmailAddress, userBEmailAddress], <EmailAddress>[userCEmailAddress], <EmailAddress>[userDEmailAddress]); 'THEN user A clicks reply all, generateRecipientsEmailAddressForComposer\n'
'SHOULD return user A email + user E email + user B email to reply, user C email to cc, user D email to bcc',
() {
final expectedResult = Tuple4(
[userAEmailAddress, userEEmailAddress, userBEmailAddress],
<EmailAddress>[userCEmailAddress],
<EmailAddress>[userDEmailAddress],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userBEmailAddress}, from: {userBEmailAddress},
@@ -143,13 +476,14 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
test( test(
'THEN user A click reply to list, generateRecipientsEmailAddressForComposer\n' 'THEN user A click reply to list, generateRecipientsEmailAddressForComposer\n'
'SHOULD return email address in mailto of List-Post to reply\n', 'SHOULD return email address in mailto of List-Post to reply\n',
() { () {
final expectedResult = Tuple3([replyToListEmailAddress], [], []); final expectedResult = Tuple4([replyToListEmailAddress], [], [], []);
final emailToReplyToList = PresentationEmail( final emailToReplyToList = PresentationEmail(
from: {userBEmailAddress}, from: {userBEmailAddress},
@@ -162,18 +496,31 @@ void main() {
final result = emailToReplyToList.generateRecipientsEmailAddressForComposer( final result = emailToReplyToList.generateRecipientsEmailAddressForComposer(
emailActionType: EmailActionType.replyToList, emailActionType: EmailActionType.replyToList,
isSender: false, isSender: false,
listPost: '<mailto:${replyToListEmailAddress.emailAddress}>', listPost: listPost,
); );
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
}); });
group('Given user A is the sender AND sends an email to user B + user E, cc to user C, bcc to user D THEN user B clicks forward', () { group(
test('generateRecipientsEmailAddressForComposer SHOULD return user user B email + user E email to reply, user C email to cc, user D email to bcc', () { 'Given user A is the sender\n'
final expectedResult = Tuple3([userBEmailAddress, userEEmailAddress], <EmailAddress>[userCEmailAddress], <EmailAddress>[userDEmailAddress]); 'AND sends an email to user B + user E, cc to user C, bcc to user D\n'
'THEN user B clicks forward',
() {
test(
'generateRecipientsEmailAddressForComposer\n'
'SHOULD return user user B email + user E email to reply, user C email to cc, user D email to bcc',
() {
final expectedResult = Tuple4(
[userBEmailAddress, userEEmailAddress],
<EmailAddress>[userCEmailAddress],
<EmailAddress>[userDEmailAddress],
<EmailAddress>[],
);
final emailToReply = PresentationEmail( final emailToReply = PresentationEmail(
from: {userAEmailAddress}, from: {userAEmailAddress},
@@ -190,6 +537,7 @@ void main() {
expect(result.value1, containsAll(expectedResult.value1)); expect(result.value1, containsAll(expectedResult.value1));
expect(result.value2, containsAll(expectedResult.value2)); expect(result.value2, containsAll(expectedResult.value2));
expect(result.value3, containsAll(expectedResult.value3)); expect(result.value3, containsAll(expectedResult.value3));
expect(result.value4, containsAll(expectedResult.value4));
}); });
}); });
}); });