TF-3189 new reply to field in the mail composer

This commit is contained in:
Florent Azavant
2024-10-30 16:02:00 +01:00
committed by Dat H. Pham
parent 78be3e4d79
commit 74fc8d5d72
20 changed files with 354 additions and 31 deletions
@@ -126,11 +126,13 @@ class ComposerController extends BaseController
final toAddressExpandMode = ExpandMode.EXPAND.obs;
final ccAddressExpandMode = ExpandMode.EXPAND.obs;
final bccAddressExpandMode = ExpandMode.EXPAND.obs;
final replyToAddressExpandMode = ExpandMode.EXPAND.obs;
final emailContentsViewState = Rxn<Either<Failure, Success>>();
final hasRequestReadReceipt = false.obs;
final fromRecipientState = PrefixRecipientState.disabled.obs;
final ccRecipientState = PrefixRecipientState.disabled.obs;
final bccRecipientState = PrefixRecipientState.disabled.obs;
final replyToRecipientState = PrefixRecipientState.disabled.obs;
final identitySelected = Rxn<Identity>();
final listFromIdentities = RxList<Identity>();
final isEmailChanged = Rx<bool>(false);
@@ -157,17 +159,20 @@ class ComposerController extends BaseController
List<EmailAddress> listToEmailAddress = <EmailAddress>[];
List<EmailAddress> listCcEmailAddress = <EmailAddress>[];
List<EmailAddress> listBccEmailAddress = <EmailAddress>[];
List<EmailAddress> listReplyToEmailAddress = <EmailAddress>[];
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
final subjectEmailInputController = TextEditingController();
final toEmailAddressController = TextEditingController();
final ccEmailAddressController = TextEditingController();
final bccEmailAddressController = TextEditingController();
final replyToEmailAddressController = TextEditingController();
final searchIdentitiesInputController = TextEditingController();
final GlobalKey<TagsEditorState> keyToEmailTagEditor = GlobalKey<TagsEditorState>();
final GlobalKey<TagsEditorState> keyCcEmailTagEditor = GlobalKey<TagsEditorState>();
final GlobalKey<TagsEditorState> keyBccEmailTagEditor = GlobalKey<TagsEditorState>();
final GlobalKey<TagsEditorState> keyReplyToEmailTagEditor = GlobalKey<TagsEditorState>();
final GlobalKey headerEditorMobileWidgetKey = GlobalKey();
final GlobalKey<DropdownButton2State> identityDropdownKey = GlobalKey<DropdownButton2State>();
final double defaultPaddingCoordinateYCursorEditor = 8;
@@ -176,10 +181,12 @@ class ComposerController extends BaseController
FocusNode? toAddressFocusNode;
FocusNode? ccAddressFocusNode;
FocusNode? bccAddressFocusNode;
FocusNode? replyToAddressFocusNode;
FocusNode? searchIdentitiesFocusNode;
FocusNode? toAddressFocusNodeKeyboard;
FocusNode? ccAddressFocusNodeKeyboard;
FocusNode? bccAddressFocusNodeKeyboard;
FocusNode? replyToAddressFocusNodeKeyboard;
StreamSubscription<html.Event>? _subscriptionOnDragEnter;
StreamSubscription<html.Event>? _subscriptionOnDragOver;
@@ -305,18 +312,23 @@ class ComposerController extends BaseController
ccAddressFocusNode = null;
bccAddressFocusNode?.dispose();
bccAddressFocusNode = null;
replyToAddressFocusNode?.dispose();
replyToAddressFocusNode = null;
toAddressFocusNodeKeyboard?.dispose();
toAddressFocusNodeKeyboard = null;
ccAddressFocusNodeKeyboard?.dispose();
ccAddressFocusNodeKeyboard = null;
bccAddressFocusNodeKeyboard?.dispose();
bccAddressFocusNodeKeyboard = null;
replyToAddressFocusNodeKeyboard?.dispose();
replyToAddressFocusNodeKeyboard = null;
searchIdentitiesFocusNode?.dispose();
searchIdentitiesFocusNode = null;
subjectEmailInputController.dispose();
toEmailAddressController.dispose();
ccEmailAddressController.dispose();
bccEmailAddressController.dispose();
replyToEmailAddressController.dispose();
uploadInlineImageWorker.dispose();
dashboardViewStateWorker.dispose();
scrollController.dispose();
@@ -480,6 +492,7 @@ class ComposerController extends BaseController
toRecipients: listToEmailAddress.toSet(),
ccRecipients: listCcEmailAddress.toSet(),
bccRecipients: listBccEmailAddress.toSet(),
replyToRecipients: listReplyToEmailAddress.toSet(),
hasRequestReadReceipt: hasRequestReadReceipt.value,
identity: identitySelected.value,
attachments: uploadController.attachmentsUploaded,
@@ -507,16 +520,21 @@ class ComposerController extends BaseController
if (bccEmailAddressController.text.isNotEmpty) {
keyBccEmailTagEditor.currentState?.closeSuggestionBox();
}
if (replyToEmailAddressController.text.isNotEmpty) {
keyReplyToEmailTagEditor.currentState?.closeSuggestionBox();
}
}
void createFocusNodeInput() {
toAddressFocusNode = FocusNode();
ccAddressFocusNode = FocusNode();
bccAddressFocusNode = FocusNode();
replyToAddressFocusNode = FocusNode();
searchIdentitiesFocusNode = FocusNode();
toAddressFocusNodeKeyboard = FocusNode();
ccAddressFocusNodeKeyboard = FocusNode();
bccAddressFocusNodeKeyboard = FocusNode();
replyToAddressFocusNodeKeyboard = FocusNode();
subjectEmailInputFocusNode = FocusNode(
onKeyEvent: PlatformInfo.isWeb ? _subjectEmailInputOnKeyListener : null,
@@ -851,7 +869,7 @@ class ComposerController extends BaseController
listCcEmailAddress = List.from(recipients.value2);
listBccEmailAddress = List.from(recipients.value3);
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty || listReplyToEmailAddress.isNotEmpty) {
isInitialRecipient.value = true;
toAddressExpandMode.value = ExpandMode.COLLAPSE;
}
@@ -866,6 +884,11 @@ class ComposerController extends BaseController
bccAddressExpandMode.value = ExpandMode.COLLAPSE;
}
if (listReplyToEmailAddress.isNotEmpty) {
replyToRecipientState.value = PrefixRecipientState.enabled;
replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
}
_updateStatusEmailSendButton();
}
@@ -883,6 +906,9 @@ class ComposerController extends BaseController
case PrefixEmailAddress.bcc:
listBccEmailAddress = List.from(newListEmailAddress);
break;
case PrefixEmailAddress.replyTo:
listReplyToEmailAddress = List.from(newListEmailAddress);
break;
default:
break;
}
@@ -891,8 +917,9 @@ class ComposerController extends BaseController
void _updateStatusEmailSendButton() {
if (listToEmailAddress.isNotEmpty
|| listCcEmailAddress.isNotEmpty
|| listBccEmailAddress.isNotEmpty
|| listCcEmailAddress.isNotEmpty) {
|| listReplyToEmailAddress.isNotEmpty) {
isEnableEmailSendButton.value = true;
} else {
isEnableEmailSendButton.value = false;
@@ -910,7 +937,8 @@ class ComposerController extends BaseController
if (toEmailAddressController.text.isNotEmpty
|| ccEmailAddressController.text.isNotEmpty
|| bccEmailAddressController.text.isNotEmpty) {
|| bccEmailAddressController.text.isNotEmpty
|| replyToEmailAddressController.text.isNotEmpty) {
_collapseAllRecipient();
_autoCreateEmailTag();
}
@@ -927,7 +955,7 @@ class ComposerController extends BaseController
return;
}
final allListEmailAddress = listToEmailAddress + listCcEmailAddress + listBccEmailAddress;
final allListEmailAddress = listToEmailAddress + listCcEmailAddress + listBccEmailAddress + listReplyToEmailAddress;
final listEmailAddressInvalid = allListEmailAddress
.where((emailAddress) => !EmailUtils.isEmailAddressValid(emailAddress.emailAddress))
.toList();
@@ -939,6 +967,7 @@ class ComposerController extends BaseController
toAddressExpandMode.value = ExpandMode.EXPAND;
ccAddressExpandMode.value = ExpandMode.EXPAND;
bccAddressExpandMode.value = ExpandMode.EXPAND;
replyToAddressExpandMode.value = ExpandMode.EXPAND;
},
showAsBottomSheet: true,
title: AppLocalizations.of(context).sending_failed,
@@ -1058,6 +1087,7 @@ class ComposerController extends BaseController
toRecipients: listToEmailAddress.toSet(),
ccRecipients: listCcEmailAddress.toSet(),
bccRecipients: listBccEmailAddress.toSet(),
replyToRecipients: listReplyToEmailAddress.toSet(),
hasRequestReadReceipt: hasRequestReadReceipt.value,
identity: identitySelected.value,
attachments: uploadController.attachmentsUploaded,
@@ -1276,6 +1306,7 @@ class ComposerController extends BaseController
toRecipients: listToEmailAddress.toSet(),
ccRecipients: listCcEmailAddress.toSet(),
bccRecipients: listBccEmailAddress.toSet(),
replyToRecipients: listReplyToEmailAddress.toSet(),
identity: identitySelected.value,
attachments: uploadController.attachmentsUploaded,
hasReadReceipt: hasRequestReadReceipt.value,
@@ -1560,6 +1591,9 @@ class ComposerController extends BaseController
case PrefixEmailAddress.bcc:
bccRecipientState.value = PrefixRecipientState.enabled;
break;
case PrefixEmailAddress.replyTo:
replyToRecipientState.value = PrefixRecipientState.enabled;
break;
default:
break;
}
@@ -1578,6 +1612,11 @@ class ComposerController extends BaseController
bccAddressFocusNode = FocusNode();
bccEmailAddressController.clear();
break;
case PrefixEmailAddress.replyTo:
replyToRecipientState.value = PrefixRecipientState.disabled;
replyToAddressFocusNode = FocusNode();
replyToEmailAddressController.clear();
break;
default:
break;
}
@@ -1587,12 +1626,14 @@ class ComposerController extends BaseController
toAddressExpandMode.value = ExpandMode.COLLAPSE;
ccAddressExpandMode.value = ExpandMode.COLLAPSE;
bccAddressExpandMode.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;
log('ComposerController::_autoCreateEmailTag:inputToEmail = $inputToEmail | inputCcEmail = $inputCcEmail | inputBccEmail = $inputBccEmail');
if (inputToEmail.trim().isNotEmpty) {
_autoCreateEmailTagForRecipientField(
@@ -1618,6 +1659,9 @@ class ComposerController extends BaseController
keyEmailTagEditor: keyBccEmailTagEditor,
);
}
if (inputReplyToEmail.isNotEmpty) {
_autoCreateReplyToEmailTag(inputReplyToEmail);
}
}
bool _isDuplicatedRecipient(String inputEmail, List<EmailAddress> listEmailAddress) {
@@ -1676,6 +1720,20 @@ class ComposerController extends BaseController
}
}
void _autoCreateReplyToEmailTag(String inputEmail) {
if (!_isDuplicatedRecipient(inputEmail, listReplyToEmailAddress)) {
final emailAddress = EmailAddress(null, inputEmail);
listReplyToEmailAddress.add(emailAddress);
isInitialRecipient.value = true;
isInitialRecipient.refresh();
_updateStatusEmailSendButton();
}
keyReplyToEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () {
keyReplyToEmailTagEditor.currentState?.closeSuggestionBox();
});
}
void _closeSuggestionBox() {
if (toEmailAddressController.text.isEmpty) {
keyToEmailTagEditor.currentState?.closeSuggestionBox();
@@ -1686,6 +1744,9 @@ class ComposerController extends BaseController
if (bccEmailAddressController.text.isEmpty) {
keyBccEmailTagEditor.currentState?.closeSuggestionBox();
}
if (replyToEmailAddressController.text.isEmpty) {
keyReplyToEmailTagEditor.currentState?.closeSuggestionBox();
}
}
void showFullEmailAddress(PrefixEmailAddress prefixEmailAddress) {
@@ -1702,6 +1763,10 @@ class ComposerController extends BaseController
bccAddressExpandMode.value = ExpandMode.EXPAND;
bccAddressFocusNode?.requestFocus();
break;
case PrefixEmailAddress.replyTo:
replyToAddressExpandMode.value = ExpandMode.EXPAND;
replyToAddressFocusNode?.requestFocus();
break;
default:
break;
}
@@ -1719,6 +1784,9 @@ class ComposerController extends BaseController
case PrefixEmailAddress.bcc:
bccAddressExpandMode.value = ExpandMode.EXPAND;
break;
case PrefixEmailAddress.replyTo:
replyToAddressExpandMode.value = ExpandMode.EXPAND;
break;
default:
break;
}
@@ -1766,6 +1834,13 @@ class ComposerController extends BaseController
);
}
break;
case PrefixEmailAddress.replyTo:
replyToAddressExpandMode.value = ExpandMode.COLLAPSE;
final inputReplyToEmail = replyToEmailAddressController.text;
if (inputReplyToEmail.isNotEmpty) {
_autoCreateReplyToEmailTag(inputReplyToEmail);
}
break;
default:
break;
}
@@ -2003,12 +2078,14 @@ class ComposerController extends BaseController
return toAddressFocusNode?.hasFocus == true ||
ccAddressFocusNode?.hasFocus == true ||
bccAddressFocusNode?.hasFocus == true ||
replyToAddressFocusNode?.hasFocus == true ||
subjectEmailInputFocusNode?.hasFocus == true;
} else if (PlatformInfo.isMobile) {
final isEditorFocused = (await richTextMobileTabletController?.isEditorFocused) ?? false;
return toAddressFocusNode?.hasFocus == true ||
ccAddressFocusNode?.hasFocus == true ||
bccAddressFocusNode?.hasFocus == true ||
replyToAddressFocusNode?.hasFocus == true ||
subjectEmailInputFocusNode?.hasFocus == true ||
isEditorFocused;
}
@@ -2052,6 +2129,8 @@ class ComposerController extends BaseController
return ccAddressFocusNode;
} else if (bccRecipientState.value == PrefixRecipientState.enabled) {
return bccAddressFocusNode;
} else if (replyToRecipientState.value == PrefixRecipientState.enabled) {
return replyToAddressFocusNode;
} else {
return subjectEmailInputFocusNode;
}
@@ -2060,6 +2139,16 @@ class ComposerController extends BaseController
FocusNode? getNextFocusOfCcEmailAddress() {
if (bccRecipientState.value == PrefixRecipientState.enabled) {
return bccAddressFocusNode;
} else if (replyToRecipientState.value == PrefixRecipientState.enabled) {
return replyToAddressFocusNode;
} else {
return subjectEmailInputFocusNode;
}
}
FocusNode? getNextFocusOfBccEmailAddress() {
if (replyToRecipientState.value == PrefixRecipientState.enabled) {
return replyToAddressFocusNode;
} else {
return subjectEmailInputFocusNode;
}
@@ -2115,6 +2204,10 @@ class ComposerController extends BaseController
listBccEmailAddress.remove(draggableEmailAddress.emailAddress);
bccAddressExpandMode.value = ExpandMode.EXPAND;
break;
case PrefixEmailAddress.replyTo:
listReplyToEmailAddress.remove(draggableEmailAddress.emailAddress);
replyToAddressExpandMode.value = ExpandMode.EXPAND;
break;
default:
break;
}
@@ -2380,6 +2473,7 @@ class ComposerController extends BaseController
toRecipients: listToEmailAddress.toSet(),
ccRecipients: listCcEmailAddress.toSet(),
bccRecipients: listBccEmailAddress.toSet(),
replyToRecipients: listReplyToEmailAddress.toSet(),
hasRequestReadReceipt: hasRequestReadReceipt.value,
identity: identitySelected.value,
attachments: uploadController.attachmentsUploaded,
@@ -2461,6 +2555,7 @@ class ComposerController extends BaseController
fromRecipientState.value = isEnabled ? PrefixRecipientState.disabled : PrefixRecipientState.enabled;
ccRecipientState.value = isEnabled ? PrefixRecipientState.disabled : PrefixRecipientState.enabled;
bccRecipientState.value = isEnabled ? PrefixRecipientState.disabled : PrefixRecipientState.enabled;
replyToRecipientState.value = isEnabled ? PrefixRecipientState.disabled : PrefixRecipientState.enabled;
}
void _handleGetEmailContentFailure(GetEmailContentFailure failure) {