From 4f5e7d460de5c616d9c7c0f3e69479e9973203a9 Mon Sep 17 00:00:00 2001 From: dab246 Date: Sat, 23 Nov 2024 03:52:01 +0700 Subject: [PATCH] TF-3294 Fix BLUE-BAR mail to attendees duplicated recipients --- .../controller/single_email_controller.dart | 22 +++---- .../calendar_attendee_extension.dart | 12 ++++ .../calendar_organizer_extension.dart | 12 ++++ .../list_email_address_extension.dart | 9 +++ .../list_email_address_extension_test.dart | 66 +++++++++++++++++++ 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 lib/features/email/presentation/extensions/calendar_attendee_extension.dart create mode 100644 lib/features/email/presentation/extensions/calendar_organizer_extension.dart diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 5d8f03c94..e3d85165d 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -92,6 +92,8 @@ import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action import 'package:tmail_ui_user/features/email/presentation/bindings/calendar_event_interactor_bindings.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/email_supervisor_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_attendee_extension.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_organizer_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_event.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart'; @@ -2184,22 +2186,20 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } void handleMailToAttendees(CalendarOrganizer? organizer, List? attendees) { - final listEmailAddressAttendees = attendees - ?.map((attendee) => EmailAddress(attendee.name?.name, attendee.mailto?.mailAddress.value)) - .toList() ?? []; + List listEmailAddressAttendees = []; if (organizer != null) { - listEmailAddressAttendees.add(EmailAddress(organizer.name, organizer.mailto?.value)); + listEmailAddressAttendees.add(organizer.toEmailAddress()); } - final listEmailAddressMailTo = listEmailAddressAttendees - .where((emailAddress) { - return emailAddress.emailAddress.isNotEmpty && - emailAddress.emailAddress != session?.username.value; - }) - .toSet() - .toList(); + final listEmailAddress = attendees + ?.map((attendee) => attendee.toEmailAddress()) + .toList() ?? []; + listEmailAddressAttendees.addAll(listEmailAddress); + + final username = mailboxDashBoardController.sessionCurrent?.username.value ?? ''; + final listEmailAddressMailTo = listEmailAddressAttendees.removeInvalidEmails(username); log('SingleEmailController::handleMailToAttendees: listEmailAddressMailTo = $listEmailAddressMailTo'); mailboxDashBoardController.goToComposer( ComposerArguments.fromMailtoUri(listEmailAddress: listEmailAddressMailTo) diff --git a/lib/features/email/presentation/extensions/calendar_attendee_extension.dart b/lib/features/email/presentation/extensions/calendar_attendee_extension.dart new file mode 100644 index 000000000..823344536 --- /dev/null +++ b/lib/features/email/presentation/extensions/calendar_attendee_extension.dart @@ -0,0 +1,12 @@ + +import 'package:jmap_dart_client/jmap/mail/calendar/properties/attendee/calendar_attendee.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; + +extension CalendarAttendeeExtension on CalendarAttendee { + EmailAddress toEmailAddress() { + return EmailAddress( + name?.name, + mailto?.mailAddress.value, + ); + } +} \ No newline at end of file diff --git a/lib/features/email/presentation/extensions/calendar_organizer_extension.dart b/lib/features/email/presentation/extensions/calendar_organizer_extension.dart new file mode 100644 index 000000000..173345b5c --- /dev/null +++ b/lib/features/email/presentation/extensions/calendar_organizer_extension.dart @@ -0,0 +1,12 @@ + +import 'package:jmap_dart_client/jmap/mail/calendar/properties/calendar_organizer.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; + +extension CalendarOrganizerExtension on CalendarOrganizer { + EmailAddress toEmailAddress() { + return EmailAddress( + name, + mailto?.value, + ); + } +} \ No newline at end of file diff --git a/model/lib/extensions/list_email_address_extension.dart b/model/lib/extensions/list_email_address_extension.dart index 8f6416794..66ffef861 100644 --- a/model/lib/extensions/list_email_address_extension.dart +++ b/model/lib/extensions/list_email_address_extension.dart @@ -69,6 +69,15 @@ extension SetEmailAddressExtension on Set? { extension ListEmailAddressExtension on List { Set asSetAddress() => map((emailAddress) => emailAddress.emailAddress).toSet(); + List removeInvalidEmails(String username) { + final Set seenEmails = {}; + return where((email) { + if (email.emailAddress.isEmpty) return false; + if (email.emailAddress == username) return false; + return seenEmails.add(email.emailAddress); + }).toList(); + } + List withoutMe(String? userName) { if (userName == null) return this; diff --git a/model/test/extensions/list_email_address_extension_test.dart b/model/test/extensions/list_email_address_extension_test.dart index 2ade025f6..7496f0036 100644 --- a/model/test/extensions/list_email_address_extension_test.dart +++ b/model/test/extensions/list_email_address_extension_test.dart @@ -1,5 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; +import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/list_email_address_extension.dart'; void main() { @@ -43,4 +44,69 @@ void main() { expect(result, 'John Doe <john@example.com>, Jane Smith <jane@example.com>'); }); }); + + group('ListEmailAddressExtension::removeInvalidEmails::test', () { + test('SHOULD remove email addresses that are empty', () { + final emails = [ + EmailAddress('Alice', 'alice@example.com'), + EmailAddress('Bob', ''), + EmailAddress('Charlie', 'charlie@example.com'), + ]; + + final validEmails = emails.removeInvalidEmails('bob@example.com'); + + expect(validEmails.length, 2); + expect(validEmails[0].emailAddress, 'alice@example.com'); + expect(validEmails[1].emailAddress, 'charlie@example.com'); + }); + + test('SHOULD remove email addresses that match the provided username', () { + final emails = [ + EmailAddress('Alice', 'alice@example.com'), + EmailAddress('Bob', 'bob@example.com'), + EmailAddress('Charlie', 'charlie@example.com'), + ]; + + final validEmails = emails.removeInvalidEmails('bob@example.com'); + + expect(validEmails.length, 2); + expect(validEmails[0].emailAddress, 'alice@example.com'); + expect(validEmails[1].emailAddress, 'charlie@example.com'); + }); + + test('SHOULD remove duplicate email addresses, keeping only the first occurrence', () { + final emails = [ + EmailAddress('Alice', 'alice@example.com'), + EmailAddress('Bob', 'bob@example.com'), + EmailAddress('Charlie', 'alice@example.com'), + EmailAddress('David', 'david@example.com'), + ]; + + final validEmails = emails.removeInvalidEmails('bob@example.com'); + + expect(validEmails.length, 2); + expect(validEmails[0].emailAddress, 'alice@example.com'); + expect(validEmails[1].emailAddress, 'david@example.com'); + }); + + test('SHOULD return an empty list if all email addresses are invalid', () { + final emails = [ + EmailAddress('Alice', ''), + EmailAddress('Bob', 'bob@example.com'), + EmailAddress('Charlie', 'bob@example.com'), + ]; + + final validEmails = emails.removeInvalidEmails('bob@example.com'); + + expect(validEmails.isEmpty, true); + }); + + test('SHOULD return an empty list for an empty input list', () { + final emails = []; + + final validEmails = emails.removeInvalidEmails('bob@example.com'); + + expect(validEmails.isEmpty, true); + }); + }); }