TF-3413 Fix ReplyAll & Reply is buggy

This commit is contained in:
dab246
2025-01-09 05:48:56 +07:00
committed by Dat H. Pham
parent 77b756cbb6
commit 2212bae095
10 changed files with 408 additions and 143 deletions
@@ -1422,6 +1422,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
mailboxRole: presentationEmail.mailboxContain?.role,
messageId: currentEmailLoaded.value?.emailCurrent?.messageId,
references: currentEmailLoaded.value?.emailCurrent?.references,
listPost: currentEmailLoaded.value?.emailCurrent?.listPost,
)
);
break;
@@ -1448,6 +1449,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
mailboxRole: presentationEmail.mailboxContain?.role,
messageId: currentEmailLoaded.value?.emailCurrent?.messageId,
references: currentEmailLoaded.value?.emailCurrent?.references,
listPost: currentEmailLoaded.value?.emailCurrent?.listPost,
)
);
break;
@@ -1,49 +1,91 @@
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/email_action_type.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/list_email_address_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
extension PresentationEmailExtension on PresentationEmail {
Tuple3<List<EmailAddress>, List<EmailAddress>, List<EmailAddress>> generateRecipientsEmailAddressForComposer({
required EmailActionType emailActionType,
Role? mailboxRole,
bool isSender = false,
String? userName,
String? listPost,
}) {
final newFromAddress = from.removeDuplicateEmails();
final newToAddress = to.removeDuplicateEmails();
final newCcAddress = cc.removeDuplicateEmails();
final newBccAddress = bcc.removeDuplicateEmails();
final newReplyToAddress = replyTo.removeDuplicateEmails();
switch (emailActionType) {
case EmailActionType.reply:
if (mailboxRole == PresentationMailbox.roleSent) {
return Tuple3(to.asList(), [], []);
} else {
final replyToAddress = replyTo.asList().isNotEmpty
? replyTo.asList()
: from.asList();
return Tuple3(replyToAddress, [], []);
}
final listReplyAddress = isSender ? newToAddress : newFromAddress;
final listReplyAddressWithoutUsername = listReplyAddress.withoutMe(userName);
return Tuple3(listReplyAddressWithoutUsername, [], []);
case EmailActionType.replyToList:
final listEmailAddress = EmailUtils.parsingListPost(listPost ?? '') ?? [];
log('PresentationEmailExtension::generateRecipientsEmailAddressForComposer:listEmailAddress = $listEmailAddress');
return Tuple3(listEmailAddress, [], []);
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost ?? '');
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 Tuple3(
listToAddressWithoutUsername,
listCcAddressWithoutUsername,
listBccAddressWithoutUsername,
);
case EmailActionType.replyAll:
if (mailboxRole == PresentationMailbox.roleSent) {
return Tuple3(to.asList(), cc.asList(), bcc.asList());
} else {
final senderReplyToAddress = replyTo.asList().isNotEmpty
? replyTo.asList()
: from.asList();
return Tuple3(
to.asList() + senderReplyToAddress,
cc.asList(),
bcc.asList(),
);
}
final recipientRecord = EmailUtils.extractRecipientsFromListPost(listPost ?? '');
final listToAddress = recipientRecord.toMailAddresses
+ newReplyToAddress
+ newFromAddress
+ newToAddress;
final listCcAddress = recipientRecord.ccMailAddresses + newCcAddress;
final listBccAddress = recipientRecord.bccMailAddresses + newBccAddress;
final listToAddressWithoutUsername = listToAddress
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
final listCcAddressWithoutUsername = listCcAddress
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
final listBccAddressWithoutUsername = listBccAddress
.toSet()
.removeDuplicateEmails()
.withoutMe(userName);
return Tuple3(
listToAddressWithoutUsername,
listCcAddressWithoutUsername,
listBccAddressWithoutUsername,
);
default:
return Tuple3(to.asList(), cc.asList(), bcc.asList());
final listToAddressWithoutUsername = newToAddress.withoutMe(userName);
final listCcAddressWithoutUsername = newCcAddress.withoutMe(userName);
final listBccAddressWithoutUsername = newBccAddress.withoutMe(userName);
return Tuple3(
listToAddressWithoutUsername,
listCcAddressWithoutUsername,
listBccAddressWithoutUsername,
);
}
}
}
@@ -122,6 +122,7 @@ class ComposerArguments extends RouterArguments {
Role? mailboxRole,
MessageIdsHeaderValue? messageId,
MessageIdsHeaderValue? references,
String? listPost,
}) => ComposerArguments(
emailActionType: EmailActionType.reply,
presentationEmail: presentationEmail,
@@ -130,6 +131,7 @@ class ComposerArguments extends RouterArguments {
mailboxRole: mailboxRole,
messageId: messageId,
references: references,
listPost: listPost,
);
factory ComposerArguments.replyToListEmail({
@@ -158,6 +160,7 @@ class ComposerArguments extends RouterArguments {
Role? mailboxRole,
MessageIdsHeaderValue? messageId,
MessageIdsHeaderValue? references,
String? listPost,
}) => ComposerArguments(
emailActionType: EmailActionType.replyAll,
presentationEmail: presentationEmail,
@@ -166,6 +169,7 @@ class ComposerArguments extends RouterArguments {
mailboxRole: mailboxRole,
messageId: messageId,
references: references,
listPost: listPost,
);
factory ComposerArguments.forwardEmail({
@@ -86,37 +86,108 @@ class EmailUtils {
}
}
static List<EmailAddress>? parsingListPost(String listPost) {
static List<String> extractMailtoLinksFromListPost(String listPost) {
try {
if (listPost.isEmpty) {
return null;
if (listPost.trim().isEmpty) return [];
final decodedInput = Uri.decodeComponent(listPost);
final mailtoRegex = RegExp(r'<(mailto:[^<>]+)>');
final matches = mailtoRegex.allMatches(decodedInput);
if (matches.isEmpty) {
log('EmailUtils::extractMailtoLinksFromListPost: Not found mailto link');
return [];
}
final regExpMailtoLinks = RegExp(r'mailto:([^>,]*)');
final allMatchesMailtoLinks = regExpMailtoLinks.allMatches(listPost);
final listMailtoLinks = allMatchesMailtoLinks
.map((match) => match.group(0))
.whereNotNull()
.toList();
log('EmailUtils::parsingListPost:listMailtoLinks: $listMailtoLinks');
if (listMailtoLinks.isNotEmpty) {
return listMailtoLinks
.map((mailto) {
final mapMailto = RouteUtils.parseMapMailtoFromUri(mailto);
final emailAddress = mapMailto[RouteUtils.paramMailtoAddress];
return emailAddress != null
? EmailAddress(null, emailAddress)
: null;
})
.whereNotNull()
.toList();
} else {
return null;
}
return matches.map((match) => match.group(1)!).toList();
} catch (e) {
logError('EmailUtils::parsingListPost:Exception = $e');
return null;
logError('EmailUtils::extractMailtoLinksFromListPost:Exception = $e');
return [];
}
}
static ({
List<EmailAddress> toMailAddresses,
List<EmailAddress> ccMailAddresses,
List<EmailAddress> bccMailAddresses,
}) extractRecipientsFromListMailtoLink(List<String> mailtoLinks) {
try {
log('EmailUtils::extractRecipientsFromListMailtoLink: mailtoLinks: $mailtoLinks:');
if (mailtoLinks.isEmpty) {
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
final toMailAddresses = <EmailAddress>[];
final ccMailAddresses = <EmailAddress>[];
final bccMailAddresses = <EmailAddress>[];
for (var mailtoLink in mailtoLinks) {
final recipientRecord = extractRecipientsFromMailtoLink(mailtoLink);
toMailAddresses.addAll(recipientRecord.toMailAddresses);
ccMailAddresses.addAll(recipientRecord.ccMailAddresses);
bccMailAddresses.addAll(recipientRecord.bccMailAddresses);
}
return (
toMailAddresses: toMailAddresses,
ccMailAddresses: ccMailAddresses,
bccMailAddresses: bccMailAddresses
);
} catch (e) {
logError('EmailUtils::extractRecipientsFromListMailtoLink:Exception = $e');
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
}
static ({
List<EmailAddress> toMailAddresses,
List<EmailAddress> ccMailAddresses,
List<EmailAddress> bccMailAddresses,
}) extractRecipientsFromMailtoLink(String mailtoLink) {
try {
log('EmailUtils::extractRecipientsFromMailtoLink:mailtoLink: $mailtoLink:');
if (mailtoLink.isEmpty) {
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
final navigationRouter =
RouteUtils.generateNavigationRouterFromMailtoLink(mailtoLink);
log('EmailUtils::extractRecipientsFromMailtoLink:navigationRouter = $navigationRouter');
return (
toMailAddresses: navigationRouter.listEmailAddress ?? [],
ccMailAddresses: navigationRouter.cc ?? [],
bccMailAddresses: navigationRouter.bcc ?? [],
);
} catch (e) {
logError('EmailUtils::extractRecipientsFromMailtoLink:Exception = $e');
return (
toMailAddresses: [],
ccMailAddresses: [],
bccMailAddresses: [],
);
}
}
static ({
List<EmailAddress> toMailAddresses,
List<EmailAddress> ccMailAddresses,
List<EmailAddress> bccMailAddresses,
}) extractRecipientsFromListPost(String listPost) {
final mailtoLinks = extractMailtoLinksFromListPost(listPost);
return extractRecipientsFromListMailtoLink(mailtoLinks);
}
}